How can I add data with objectBox? - flutter

The data must be consumed from the ObjectBox's database and loaded into the dependent lists. which is already being done.
I have to add an "Add Region" button that already has the function that sends me to the other view to add a region and communicate with the ObjectBox's database. Which is being done, but not how I would like it to be, since the idea is to add the Region with an auto-incrementing id and the communes are associated with that id when added.
When the data is already added, it should automatically sync or have a button that does that sync job
Controller
class HomeController extends GetxController {
var regionList = <RegionModel>[].obs;
List<ComunaModel> comunas = <ComunaModel>[].obs;
List<RegionModel> get regiones => regionList.value;
final dropdownRegiones = [].obs;
final dropdownComunas = <String>[].obs;
#override
void onInit() {
regionList.bindStream(objectbox.getRegiones());
}
}
ComunaModel
#Entity()
class ComunaModel {
#Id()
int comunaId = 0;
late String comunaNombre;
final region = ToOne<RegionModel>();
ComunaModel({this.comunaId = 0, required this.comunaNombre});
}
RegionModel
#Entity()
class RegionModel {
#Id()
int regionId = 0;
late String regionNombre;
late int regionCodigo;
#Backlink()
final comunas = ToMany<ComunaModel>();
RegionModel(
{this.regionId = 0,
required this.regionNombre,
required this.regionCodigo});
}
ObjectBox
class ObjectBox {
late final Store store;
late final Admin admin;
late final Box<RegionModel> regionBox;
late final Box<ComunaModel> comunaBox;
ObjectBox._create(this.store) {
if (Admin.isAvailable()) {
admin = Admin(store);
}
regionBox = Box<RegionModel>(store);
comunaBox = Box<ComunaModel>(store);
if (regionBox.isEmpty()) {
_putData();
}
}
get regionController => null;
get comunaController => null;
/// Create an instance of ObjectBox to use throughout the app.
static Future<ObjectBox> create() async {
// Note: on desktop systems this returns the users documents directory,
// so make sure to create a unique sub-directory.
// On mobile using the default (not supplying any directory) is typically
// fine, as apps have their own directory structure.
final documentsDirectory = await getApplicationDocumentsDirectory();
final databaseDirectory =
p.join(documentsDirectory.path, "objectbox example");
// Future<Store> openStore() {...} is defined in the generated objectbox.g.dart
final store = await openStore(directory: databaseDirectory);
return ObjectBox._create(store);
}
void _putData() {
RegionModel region =
RegionModel(regionNombre: 'Arica y Parinacota', regionCodigo: 1);
ComunaModel comuna = ComunaModel(comunaNombre: 'Arica');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'Camarones');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'General Lagos');
region.comunas.add(comuna);
comuna = ComunaModel(comunaNombre: 'Putre');
region.comunas.add(comuna);
regionBox.put(region);
//final customerId = store.box<RegionModel>().put(region);
}
Stream<List<RegionModel>> getRegiones() {
final qBuilderRegiones = regionBox.query()
..order(RegionModel_.regionId, flags: Order.descending);
return qBuilderRegiones
.watch(triggerImmediately: true)
.map((query) => query.find());
}
Future<void> addRegionNombre(String regionNombre, int regionCodigo) async {
RegionModel region =
RegionModel(regionNombre: regionNombre, regionCodigo: regionCodigo);
regionBox.put(region);
}
Future<void> addComunaNombre(String comunaNombre) async {
ComunaModel comuna = ComunaModel(comunaNombre: comunaNombre);
comunaBox.put(comuna);
}
}
HomePage
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
var title = "DropDownSearch Con ObjectBox";
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(26),
child: StreamBuilder<List<RegionModel>>(
stream: objectbox.getRegiones(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
Obx(
() {
HomeController controller = Get.put(HomeController());
return controller.regionList.isEmpty
? const Center(
child: Text('No hay regiones'),
)
: DropdownSearch<RegionModel>(
popupProps:
const PopupProps.menu(showSearchBox: true),
asyncItems: (String filter) async {
return controller.regionList;
},
itemAsString: (RegionModel u) => u.regionNombre,
onChanged: (RegionModel? data) {
if (data?.comunas != null &&
data!.comunas.isNotEmpty) {
controller.comunas = data.comunas;
print(controller.comunas
.map((e) => e.comunaNombre)
.toList());
}
controller.dropdownComunas.value = controller
.comunas
.map((e) => e.comunaNombre)
.toList();
},
dropdownDecoratorProps:
const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Region",
hintText: "Seleccione una region",
),
),
);
},
),
Obx(
() => DropdownSearch<String>(
popupProps: const PopupProps.menu(
showSelectedItems: true,
),
items: controller.dropdownComunas.value,
dropdownDecoratorProps: const DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Comuna",
hintText: "Seleccione una comuna",
),
),
onChanged: print,
selectedItem: controller.comunas.isNotEmpty == true
? controller.comunas[0].comunaNombre
: null),
),
Container(
padding: const EdgeInsets.all(100),
child: Align(
alignment: Alignment.bottomRight,
child: Column(children: [
FloatingActionButton.extended(
key: const Key('add'),
label: const Text('Add Region'),
heroTag: null,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const AddPage()));
},
),
]),
),
),
],
);
} else {
return const CircularProgressIndicator();
}
},
),
),
),
);
}
}
AddPage
class AddPage extends StatefulWidget {
const AddPage({super.key});
#override
State<AddPage> createState() => _AppPageState();
}
class _AppPageState extends State<AddPage> {
#override
Widget build(BuildContext context) {
const appTitle = "Agregar Region";
return // scaffold
Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: Padding(
padding: const EdgeInsets.all(26),
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: "Region",
hintText: "Ingrese una region",
),
onFieldSubmitted: (String value) {
objectbox.addRegionNombre(value, 1);
},
onEditingComplete: () {
Get.snackbar("Region", "Region agregada");
},
),
TextFormField(
decoration: const InputDecoration(
labelText: "Comuna",
hintText: "Ingrese una comuna",
),
onFieldSubmitted: (String value) {
objectbox.addComunaNombre(value);
},
onEditingComplete: () {
Get.snackbar("Comuna", "Comuna agregada");
},
),
],
),
),
);
}
}
Update: I created this method so that it will add region and commune, but it doesn't work
Future<List<int>> addRegionNombre(
String regionNombre, int regionCodigo) async {
RegionModel region =
RegionModel(regionNombre: regionNombre, regionCodigo: regionCodigo);
final regionId = regionBox.put(region);
List<int> regionComunaId = [];
regionComunaId.add(regionId);
for (var i = 0; i < region.comunas.length; i++) {
region.comunas[i].comunaId = regionId;
final comunaId = comunaBox.put(region.comunas[i]);
regionComunaId.add(comunaId);
}
return regionComunaId;
}

You can achieve this by using a stream to keep track of the latest state of the database and feeding that into a ListView builder.
You can take a look at this code example to see how I do that:
Setting up Stream inside ObjectBox Class
Generating a List using that stream

Related

The state of my object is not stored properly

I want to mark my object as favorite.
I have a list of object <RobotAnimation> which is displayed in a ListView. The class have two fields: title and isFavorite. Marking an object as a favorite works, but there is a problem when it comes to storing that state. When I perform a search of all items, after selecting an item as favorite, my favorite items are not being remembered. It seems like the state is being discarded.
What can I do to fix this problem?
Here's what's going on:
Here's my code:
class RobotAnimation {
String title;
bool isFavorite;
RobotAnimation({required this.title, this.isFavorite = false});
#override
String toString() {
return '{Title: $title, isFavortite: $isFavorite}';
}
}
class Animations extends StatefulWidget {
const Animations({super.key});
#override
State<Animations> createState() => _AnimationsState();
}
class _AnimationsState extends State<Animations> with TickerProviderStateMixin {
late TabController _tabController;
List<RobotAnimation> animations = [];
List<RobotAnimation> favoriteAnimations = [];
List<String> results = store.state.animations;
List<String> defaultFavorites = [];
List<RobotAnimation> getAnimationList(
List<String> animations, List<String> favorites) {
List<RobotAnimation> robotAnimations = [];
for (var animation in animations) {
bool isFav = false;
for (var favorite in favorites) {
if (favorite == animation) {
isFav = true;
}
}
robotAnimations.add(RobotAnimation(title: animation, isFavorite: isFav));
}
return robotAnimations;
}
List<RobotAnimation> filterFavorites() {
List<RobotAnimation> filtered = favoriteAnimations;
animations.where((element) => element.isFavorite == true).toList();
return filtered;
}
void filterSearchResults(String query) {
List<RobotAnimation> searchList =
getAnimationList(results, defaultFavorites);
log('query: $query');
List<RobotAnimation> filteredList = searchList
.where((element) =>
element.title.toLowerCase().contains(query.toLowerCase()))
.toList();
log(searchList.toString());
log(filteredList.toString());
setState(() => animations = filteredList);
}
#override
void initState() {
animations = getAnimationList(results, defaultFavorites);
super.initState();
}
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _Props>(
converter: (store) => _mapStateToProps(store),
builder: (_, props) {
return Scaffold(
body: TabBarView(
...
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
onChanged: filterSearchResults,
decoration: const InputDecoration(
labelText: 'Search',
hintText: 'Search animation',
prefixIcon: Icon(Icons.search),
),
),
Expanded(
child: ListView.separated(
itemCount: animations.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return ListTile(
onTap: () {
props.socket?.animation(IAnimation(
animation: animations[index].title));
},
title: ExtendedText(
animations[index].title,
maxLines: 1,
overflowWidget: const TextOverflowWidget(
position: TextOverflowPosition.middle,
align: TextOverflowAlign.center,
child: Text(
'...',
overflow: TextOverflow.ellipsis,
),
),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
setState(() {
animations[index].isFavorite
? animations[index].isFavorite = false
: animations[index].isFavorite = true;
});
},
icon: animations[index].isFavorite
? Icon(
Icons.favorite,
color: Colors.red.shade500,
)
: Icon(
Icons.favorite_border,
color: Colors.grey.shade500,
),
),
],
),
);
},
),
)
],
),
),
],
),
);
},
);
}
}
class _Props {
final Connection? socket;
final List<String> animations;
_Props({
required this.socket,
required this.animations,
});
}
_Props _mapStateToProps(Store<AppState> store) {
return _Props(
socket: store.state.socket,
animations: store.state.animations,
);
}
Try this inside your IconButton onPressed-Method:
setState(() {
if (animations[index].isFavorite) {
animations[index].isFavorite = false
defaultFavorites.remove(animations[index].title)
} else {
animations[index].isFavorite = true;
defaultFavorites.add(animations[index].title)
}
});
It seems like you always generate a new list of animations based on the two lists List<String>.

Flutter Dynamic Searching appBar

I'm trying to create a dynamic search in the appBar, but unfortunately I'm not succeeding and I have an error on this line.
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
Full code:
import 'package:favspot/src/views/places_card.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../services/places.dart';
import 'dart:async';
class SeachPlaces extends StatefulWidget {
#override
State<SeachPlaces> createState() => _SeachPlacesState();
}
class _SeachPlacesState extends State<SeachPlaces> {
List<Object> _listPlaces = [];
List<Object> _searchSearch = [];
#override
initState(){
_searchSearch = _listPlaces;
super.initState();
}
void _runFilter(String enteredKeyword) {
List<Object> results = [];
if (enteredKeyword.isEmpty) {
results = _listPlaces;
} else {
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();
setState((){
_searchSearch = results;
});
}
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
getPlacesList();
}
TextEditingController? _textEditngController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
width: double.infinity,
height: 40,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30)),
child: Center(
child:TextField(
onChanged: (value) => _runFilter(value),
controller: _textEditngController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(5),
prefixIcon: Icon(Icons.search),
suffix: IconButton(
icon: Icon(Icons.clear),
onPressed: null,
),
hintText: 'Searching...',
border: InputBorder.none,
),
),
),
),
),
body: SafeArea(
child: ListView.builder(
itemCount: _searchSearch.length,
itemBuilder: (context, index) {
return PlacesCard(_searchSearch[index] as Places);
},
)
),
);
}
Future getPlacesList() async {
var data = await FirebaseFirestore.instance
.collection('places')
.orderBy('city', descending: false)
.get();
setState(() {
_listPlaces = List.from(data.docs.map((doc) => Places.fromSnapshot(doc)));
});
}
}
Here is de class Places
class Places {
String? place;
String? city;
String? status;
Places();
Map<String, dynamic> toJson() => {
'city' : city,
'status' : status,
};
Places.fromSnapshot(snapshot) :
place = snapshot.id,
city = snapshot.data()['city'],
status = snapshot.data()['status'];
}
Consider to use List<Places> _listPlaces instead of List<Object> _listPlaces, so you can access properties like this:
results = _listPlaces.where((places) => places.place != null && places.place!.toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();

type '_Type' is not a subtype of type 'String'

So i am a beginner in flutter and am trying to learn via tutorials, so here I am trying to make todo app using sqflite and everything is perfect and no error is shown in the editor but on clicking floating action button in notelist file it shows this error-
The following _TypeError was thrown building Builder:
type '_Type' is not a subtype of type 'String'
heres my main.dart file
void main() {
runApp(MaterialApp(
home: NoteList(),
));
}
here notelist
class NoteList extends StatefulWidget {
const NoteList({Key? key}) : super(key: key);
#override
_NoteListState createState() => _NoteListState();
}
class _NoteListState extends State<NoteList> {
int count = 0;
DatabaseHelper databaseHelper = DatabaseHelper();
late List<Note> noteList;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Note List'),
),
body: getNoteListView(),
floatingActionButton: FloatingActionButton(
onPressed: () {
debugPrint('fab clicked');
navigateToDetail(Note('', '', 2 ,''),'Add Note');
},
child: Icon(Icons.add),
),
);
}
ListView getNoteListView(){
return ListView.builder(
itemCount: count,
itemBuilder: (context, index){
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
backgroundColor: getPriorityColor(this.noteList[index].priority),
child: getPriorityIcon(this.noteList[index].priority),
),
title: Text(this.noteList[index].title!,),
subtitle: Text(this.noteList[index].date!),
trailing: IconButton(onPressed: (){
_delete(context, noteList[index]);
},
icon: Icon(Icons.delete),
),
onTap: (){
debugPrint('tapped');
navigateToDetail(noteList[index],'Edit Note');
},
),
);
}
);
}
void navigateToDetail(Note note, String title) async{
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return NoteDetail(appBarTitle: Title, note: note);
}));
if (result == true) {
updateListView();
}
}
// Returns the priority color
Color getPriorityColor(int? priority) {
switch (priority) {
case 1:
return Colors.red;
break;
case 2:
return Colors.yellow;
break;
default:
return Colors.yellow;
}
}
// Returns the priority icon
Icon getPriorityIcon(int? priority) {
switch (priority) {
case 1:
return Icon(Icons.play_arrow);
break;
case 2:
return Icon(Icons.keyboard_arrow_right);
break;
default:
return Icon(Icons.keyboard_arrow_right);
}
}
void _delete(BuildContext context, Note note) async {
int? result = await databaseHelper.deleteNote(note.id);
if (result != 0) {
_showSnackBar(context, 'Note Deleted Successfully');
updateListView();
}
}
void _showSnackBar(BuildContext context, String message) {
final snackBar = SnackBar(content: Text(message));
Scaffold.of(context).showSnackBar(snackBar);
}
void updateListView() {
final Future<Database> dbFuture = databaseHelper.initializeDatabase();
dbFuture.then((database) {
Future<List<Note>> noteListFuture = databaseHelper.getNoteList();
noteListFuture.then((noteList) {
setState(() {
this.noteList = noteList;
this.count = noteList.length;
});
});
});
}
}
and heres notedetail file
class NoteDetail extends StatefulWidget {
final Note note;
final appBarTitle;
NoteDetail( {Key? key,required this.appBarTitle, required this.note}) : super(key: key);
#override
_NoteDetailState createState() => _NoteDetailState(this.note, this.appBarTitle);
}
class _NoteDetailState extends State<NoteDetail> {
static var _priorities = ['High', 'Low'];
DatabaseHelper helper = DatabaseHelper();
TextEditingController titleController = TextEditingController();
TextEditingController descController = TextEditingController();
String appBarTitle;
Note note;
_NoteDetailState(this.note , this.appBarTitle);
#override
Widget build(BuildContext context) {
titleController.text = note.title!;
descController.text = note.description!;
return Scaffold(
appBar: AppBar(
title: Text(appBarTitle),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
ListTile(
title: DropdownButton(
items: _priorities.map((dropDownStringItem) {
return DropdownMenuItem (
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}).toList(),
value: getPriorityAsString(note.priority),
onChanged: (valueSelectedByUser) {
setState(() {
debugPrint('User selected $valueSelectedByUser');
updatePriorityAsInt(valueSelectedByUser);
});
}
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: titleController,
onChanged: (value) {
debugPrint('something changed in the title textfield ');
updateTitle();
},
decoration: InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: descController,
onChanged: (value) {
debugPrint('something changed in the description textfield ');
updateDescription();
},
decoration: InputDecoration(
labelText: 'Description',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
Container(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
debugPrint('add button clicked');
_save();
}, child: Text('Save',
style: TextStyle(
fontSize: 18
),
)
),
),
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
_delete();
debugPrint('Delete button clicked');
}, child: Text('Delete',
style: TextStyle(
fontSize: 18
),)),
),
],
),
)
],
),
),
);
}
// Convert int priority to String priority and display it to user in DropDown
String getPriorityAsString(int? value) {
String priority = '';
switch (value) {
case 1:
priority = _priorities[0]; // 'High'
break;
case 2:
priority = _priorities[1]; // 'Low'
break;
}
return priority;
}
// Convert the String priority in the form of integer before saving it to Database
void updatePriorityAsInt(var value) {
switch (value) {
case 'High':
note.priority = 1;
break;
case 'Low':
note.priority = 2;
break;
}
}
// Update the title of Note object
void updateTitle(){
note.title = titleController.text;
}
// Update the description of Note object
void updateDescription() {
note.description = descController.text;
}
void _delete() async {
moveToLastScreen();
// Case 1: If user is trying to delete the NEW NOTE i.e. he has come to
// the detail page by pressing the FAB of NoteList page.
if (note.id == null) {
_showAlertDialog('Status', 'No Note was deleted');
return;
}
// Case 2: User is trying to delete the old note that already has a valid ID.
int? result = await helper.deleteNote(note.id);
if (result != 0) {
_showAlertDialog('Status', 'Note Deleted Successfully');
} else {
_showAlertDialog('Status', 'Error Occured while Deleting Note');
}
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
void _showAlertDialog(String title, String message) {
AlertDialog alertDialog = AlertDialog(
title: Text(title),
content: Text(message),
);
showDialog(
context: context,
builder: (_) => alertDialog
);
}
// Save data to database
void _save() async {
moveToLastScreen();
note.date = DateFormat.yMMMd().format(DateTime.now());
int? result;
if (note.id != null) { // Case 1: Update operation
result = await helper.updateNote(note);
} else { // Case 2: Insert Operation
result = await helper.insertNote(note);
}
if (result != 0) { // Success
_showAlertDialog('Status', 'Note Saved Successfully');
} else { // Failure
_showAlertDialog('Status', 'Problem Saving Note');
}
}
}
This looks like a spelling mistake.
void navigateToDetail(Note note, String title) async{
...
// change Title into title
return NoteDetail(appBarTitle: title, note: note);
...

Flutter Barcode Scan Result Is'nt Listed

My application is to search through the list of books. Two different variables (book name or barcode) can be used while searching. There is no problem when searching by name. but when searching with barcode scanning, no results are listed. When I type the barcode manually, the application still works without any problems.
Can u help me?
Manually entered barcode: https://i.stack.imgur.com/njtLA.png
Barcode scan result : https://i.stack.imgur.com/ZsGot.png
My code here..
import 'package:fff/book_tile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:fff/book_model.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _controller = new TextEditingController();
List<Book> _booksForDisplay = [];
List<Book> _books = [];
#override
void initState() {
super.initState();
fetchBooks().then((value) {
setState(() {
_books.addAll(value);
_booksForDisplay = _books;
print(_booksForDisplay.length);
});
});
}
Future _scan(BuildContext context) async {
String barcode = await FlutterBarcodeScanner.scanBarcode(
'#ff0000',
'İptal',
true,
ScanMode.BARCODE
);
_controller.text = barcode;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 80,
title: Padding(
padding: EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40)
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: TextFormField(
textAlignVertical: TextAlignVertical.center,
controller: _controller,
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.search),
suffixIcon: IconButton(
icon: Icon(FontAwesomeIcons.barcode),
onPressed: (){
_scan(context);
},
)
),
onChanged: (string){
string = string.toLowerCase();
setState(() {
_booksForDisplay = _books.where((b){
var bName = b.name!.toLowerCase();
var bBarcode = b.barcode!.toLowerCase();
return bName.startsWith(string) || bBarcode.startsWith(string);
}).toList();
});
},
),
),
),
),
),
body: SafeArea(
child: Container(
child: _controller.text.isNotEmpty ? new ListView.builder(
itemCount: _booksForDisplay.length,
itemBuilder: (context, index){
return BookTile(book: this._booksForDisplay[index]);
},
)
:
Center(
child: Text('Searching..'),
)
),
)
);
}
}
I think you only need a listener for your TextEditingController. And you should write your onChanged method inside that listener.
#override
void initState() {
super.initState();
fetchBooks().then((value) {
setState(() {
_books.addAll(value);
_booksForDisplay = _books;
print(_booksForDisplay.length);
});
});
_controller.addListener(() {
print(_controller.text);
var string = _controller.text.toLowerCase();
setState(() {
_booksForDisplay = _books.where((b){
var bName = b.name!.toLowerCase();
var bBarcode = b.barcode!.toLowerCase();
return bName.startsWith(string) ||
bBarcode.startsWith(string);
}).toList();
});
});
}

Flutter-- How to add a search bar to search in the list

I have a countries list which shows the time for the countries. Please let me know how to add the search bar.You can see the WorldTime list in the code. So it's displaying the countries name with the image. The code is as follows.
Choose_Location.dart
class Chooselocation extends StatefulWidget {
#override
_ChooseLocationState createState() => _ChooseLocationState();
}
class _ChooseLocationState extends State<Chooselocation> {
List<WorldTime> locations =[
WorldTime(url:'Europe/London', location: 'London', flag: 'England.png'),
WorldTime(url:'Europe/Berlin', location: 'Berlin', flag: 'Germany.jpg'),
WorldTime(url:'Africa/Cairo', location: 'Cairo', flag: 'Egypt.jpg'),
WorldTime(url:'Africa/Nairobi', location: 'Nairobi', flag: 'Kenya.jpg'),
WorldTime(url:'Asia/Jakarta', location: 'Seoul', flag: 'Indonesia.jpg'),
WorldTime(url:'Asia/Qatar', location: 'Qatar', flag: 'Qatar.png'),
WorldTime(url:'Africa/Khartoum', location: 'Sudan', flag: 'Sudan.jpg'),
WorldTime(url:'Asia/Karachi', location: 'Pakistan', flag: 'Pakistan.png'),
WorldTime(url:'America/New_York', location: 'USA', flag: 'USA.jpg'),
];
void updatetime(index) async{
WorldTime instance = locations[index];
await instance.getTime();
// mnavigate to home screen
Navigator.pop(context,{
'location':instance.location,
'flag':instance.flag,
'time':instance.time,
'isDaytime':instance.isDaytime,
});
}
#override
Widget build(BuildContext context) {
print('Build state function');
return Scaffold(
backgroundColor: Colors.blueGrey[200],
appBar: AppBar(
title:Text('Choose a Location'),
centerTitle:true,
elevation:0,
),
body: ListView.builder(
itemCount:locations.length,
itemBuilder: (context,index){
return Padding(
padding: const EdgeInsets.symmetric(vertical:1.0, horizontal: 4.0),
child: Card(child: ListTile(
onTap: (){
updatetime(index);
print(locations[index].location);
},
title:Text(locations[index].location),
leading: CircleAvatar(
backgroundImage: AssetImage('assets/${locations[index].flag}')),
),
),
);
}
),
);
}
}
You can see the image of the UI below.
In my case, i don't have WorldTime model class, so i used List<String> for filter functionality. You can use your model class for filter your list.
class Chooselocation extends StatefulWidget {
#override
_ChooseLocationState createState() => _ChooseLocationState();
}
class _ChooseLocationState extends State<Chooselocation> {
List<String> locations = [
'London',
'Berlin',
'Cairo',
'Nairobi',
'Seoul',
'Qatar',
'Sudan',
'Pakistan',
'USA'
];
List<String> locationList;
var locationDataList = List<String>();
final TextEditingController _filter = TextEditingController();
String _searchText = "";
Icon _searchIcon = new Icon(Icons.search);
Widget _appBarTitle;
void _searchPressed(String title) {
setState(() {
if (this._searchIcon.icon == Icons.search) {
this._searchIcon = new Icon(Icons.close);
this._appBarTitle = new TextField(
style: setTextStyle(),
controller: _filter,
decoration: new InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white)),
prefixIcon: new Icon(
Icons.search,
color: Colors.white,
),
hintText: 'Search...',
hintStyle: setTextStyle()),
);
} else {
this._searchIcon = new Icon(Icons.search);
this._appBarTitle = new Text(title);
_filter.clear();
this._appBarTitle = null;
}
});
}
setTextStyle() {
return TextStyle(color: Colors.white);
}
#override
void initState() {
// TODO: implement initState
super.initState();
print("object");
_filter.addListener(() {
if (_filter.text.isEmpty) {
setState(() {
_searchText = "";
updateFilter(_searchText);
});
} else {
setState(() {
_searchText = _filter.text;
updateFilter(_searchText);
});
}
});
}
void updateFilter(String text){
print("updated Text: ${text}");
filterSearchResults(text);
}
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(locationList);
print("List size : " + dummySearchList.length.toString());
if(query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if(item.toLowerCase().contains(query.toLowerCase())) {
dummyListData.add(item);
}
});
setState(() {
locationDataList.clear();
locationDataList.addAll(dummyListData);
});
return;
} else {
setState(() {
locationDataList.clear();
locationDataList.addAll(locations);
});
}
}
#override
Widget build(BuildContext context) {
if (locationList == null) {
locationList = List<String>();
locationList.addAll(locations);
locationDataList.addAll(locationList);
}
return Scaffold(
backgroundColor: Colors.blueGrey[200],
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: _searchIcon,
onPressed: () {
_searchPressed("Choose Location");
},
tooltip: "Search",
)
],
title: _appBarTitle == null ? Text('Choose a Location') : _appBarTitle,
centerTitle: true,
elevation: 0,
),
body: ListView.builder(
itemCount: locationDataList.length,
itemBuilder: (context, index) {
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 1.0, horizontal: 4.0),
child: Card(
child: ListTile(
title: Text(locationDataList[index]),
leading: CircleAvatar(),
),
),
);
}),
);
}
}
Output of above code would be :
Hope this may help you :)
Create a TextField for enter searchKey by user
Use:
List<WorldTime> filteredLocations = locations.where(element => element.location == **searchKey**).toList();
Its return a list of all elements that contains searhchKey