how to use TextEditingController from Autocomplete widget Flutter - flutter

I need to use TexteditingController of the widget "autocomplete".
is to use the clear function when a stepper changes stage
I need to do that since if I go back a stage the text entered previously remains
this is the autocomplete code:
Autocomplete<Profesional>(
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<Profesional> onSelected,
Iterable<Profesional> options) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
child: SizedBox(
height: 200.0,
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final Profesional option =
options.elementAt(index);
return GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option.cod),
),
);
},
),
),
),
);
},
optionsBuilder: (TextEditingValue query) {
return viewModel.efectores.where((efector) {
return efector.cod
.toLowerCase()
.contains(query.text.toLowerCase()) ||
efector.nombre
.toLowerCase()
.contains(query.text.toLowerCase());
});
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextFormField(
controller: textEditingController,
decoration: const InputDecoration(
hintText: 'Seleccione Efector',
),
autofocus: true,
focusNode: focusNode,
onFieldSubmitted: (String value) {
onFieldSubmitted();
},
);
},
displayStringForOption: (efector) {
return efector.cod + ' - ' + efector.nombre;
},
onSelected: (efector) {
viewModel.efector = efector;
}),

You can use RawAutocomplete instead of Autocomplete.
In this case, you can pass your own TextEditingController and FocusNode. Then use the TextEditingController clear method to clear text if when needed.
If you need to clear the autocomplete view from the parent widget user global key.
See sample code here:
class CustomAutocomplete extends StatelessWidget {
final TextEditingController _textEditingController = TextEditingController();
final FocusNode _focusNode = FocusNode();
final GlobalKey _autocompleteKey = GlobalKey();
final List<String> _options = <String>[
'aardvark',
'bobcat',
'chameleon',
];
CustomAutocomplete({Key? key}) : super(key: key);
void clear() {
_textEditingController.clear();
}
#override
Widget build(BuildContext context) {
return RawAutocomplete<String>(
key: _autocompleteKey,
focusNode: _focusNode,
textEditingController: _textEditingController,
optionsBuilder: (TextEditingValue textEditingValue) {
return _options.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
}).toList();
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
return Material(
elevation: 4.0,
child: ListView(
children: options
.map((String option) => GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option),
),
))
.toList(),
),
);
},
);
}
}

Related

error: The argument type 'Null' can't be assigned to the parameter type 'Map<String, dynamic>'

I am writing my first Flutter App with some online tutorials and I found error that I can't fix it.
I am trying to add Navigation by Navigator, but I can't understand why it doesn't work.
Once I am using Navigator in GestureDetector and it works fine, but I don't know what I supposed to do in floatingActionButton to make it work the same way. Note(NoteMode.Adding, null) probably should be something else instead null, because this null is making error (error from title). Can someone explain me what I am doing wrong and what I don't undarstand
Note List
#override
_NoteListState createState(){return _NoteListState();}
}
class _NoteListState extends State<NoteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Notes"),
),
body: FutureBuilder(
future: NoteProvider.getNoteList(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final notes = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) =>
Note(NoteMode.Editing, (notes as dynamic)[index]))
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, bottom: 30.0, left: 13, right: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_NoteTitle((notes as dynamic)[index]['title']),
Container(height: 3,),
_NoteText((notes as dynamic)[index]['text']),
],
),
),
),
);
},
itemCount: notes.length,
);
}
return Center(child: CircularProgressIndicator());
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, null)));
},
child: Icon(Icons.add),
),
);
}
}
Note
enum NoteMode{
Editing,
Adding
}
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic> note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}
class _NoteState extends State<Note> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _textController = TextEditingController();
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
#override
void didChangeDependencies(){
if(widget.noteMode == NoteMode.Editing){
_titleController.text = widget.note['title'];
_textController.text = widget.note['text'];
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.noteMode == NoteMode.Adding ? 'Add note' : 'Edit note',
),
),
body: Padding(
padding: const EdgeInsets.all(40.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: "Note title",
),
),
Container(height: 8,),
TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Note text",
),
),
Container(height: 15,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_NoteButton('SAVE', Colors.lightBlue, (){
final title = _titleController.text;
final text = _textController.text;
if(widget.noteMode == NoteMode.Adding){
NoteProvider.insertNote({
'title': title,
'text': text
});
} else if (widget.noteMode == NoteMode.Editing){
NoteProvider.updateNote( {
'id': widget.note['id'],
'title': _titleController.text,
'text': _textController.text,
});
}
Navigator.pop(context);}),
_NoteButton('DISCARD', Colors.grey, (){Navigator.pop(context);}),
widget.noteMode == NoteMode.Editing ?
_NoteButton('DELETE', Colors.redAccent, () async {
await NoteProvider.deleteNote(widget.note['id']);
Navigator.pop(context);})
: Container(),
],
)
],
),
),
);
}
}
Either you have to pass Map in place of null because you are receiving a Map on that page
Navigator.push(context, MaterialPageRoute(builder: (context) => Note(NoteMode.Adding, {"key":"value"})));
or you have to make Map nullable as
class Note extends StatefulWidget{
final NoteMode noteMode;
final Map<String, dynamic>? note;
Note(this.noteMode, this.note,);
#override
State<Note> createState() => _NoteState();
}

Flutter raw autocomplete suggestions get hidden under soft keyboard

I'm creating a raw auto complete widget.
The issue is if the widget is at the center or around the bottom of the screen, when I start typing the auto suggestions shown gets hidden under the soft keyboard. How to build the optionsViewBuilder to overcome the hiding of the options under the keyboard?
Sample source code:
class AutoCompleteWidget extends StatefulWidget {
const AutoCompleteWidget(
Key key,
) : super(key: key);
#override
_AutoCompleteWidgetState createState() => _AutoCompleteWidgetState();
}
class _AutoCompleteWidgetState extends State<AutoCompleteWidget> {
late TextEditingController _textEditingController;
String? _errorText;
final FocusNode _focusNode = FocusNode();
final GlobalKey _autocompleteKey = GlobalKey();
List<String> _autoSuggestions = ['abc', 'def', 'hij', 'aub', 'bted' 'donfr', 'xyz'];
#override
void initState() {
super.initState();
_textEditingController = TextEditingController();
}
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return RawAutocomplete<String>(
key: _autocompleteKey,
focusNode: _focusNode,
textEditingController: _textEditingController,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return _autoSuggestions;
}
return _autoSuggestions.where((dynamic option) {
return option
.toString()
.toLowerCase()
.startsWith(textEditingValue.text.toLowerCase());
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<String> onSelected, Iterable<String> options) {
return Material(
elevation: 4.0,
child: ListView(
children: options
.map((String option) => GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option),
),
))
.toList(),
),
);
},
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onSubmitted,
) {
return Card(
elevation: (null == _errorText ? 8 : 0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
child: TextField(
controller: textEditingController,
focusNode: focusNode,
),
);
},
);
}
}
A solution I came up with was using was building my own version of a simple autocomplete widget using a TextFormField and setting scrollPadding on it. I'm showing the results in a container with a set height that works with that padding.
#override
Widget build(BuildContext context) {
return ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
// THE AUTOCOMPLETE INPUT FIELD
TextFormField(
focusNode: _focusNode,
scrollPadding: const EdgeInsets.only(bottom: 300),
maxLines: null,
key: const ValueKey('company_address'),
autocorrect: false,
enableSuggestions: false,
controller: widget.textEditingController,
validator: (value) {
if (value!.isEmpty) {
return _i10n.enterAName;
}
return null;
},
decoration: InputDecoration(
labelText: widget.labelText,
),
textInputAction: TextInputAction.next,
onChanged: (_) {
_handleChange();
widget.onChange();
},
onTap: () {
setState(() {
_showAutocompleteSuggestions = true;
});
},
),
const SizedBox(
height: 5.0,
),
// THE AUTOCOMPLETE RESULTS
if (_showAutocompleteSuggestions)
Container(
// height: _autocompleteHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: const [
BoxShadow(blurRadius: 10.0, color: Colors.black12)
],
color: Colors.white,
),
constraints: const BoxConstraints(maxHeight: 200.0),
child: Scrollbar(
child: SingleChildScrollView(
child: Column(children: [
if (_autocompleteSuggestions.isEmpty)
const ListTile(
title: Text('No results'),
)
else
..._autocompleteSuggestions.map((_autocompleteSuggestion) =>
Material(
child: InkWell(
onTap: () {
_handleSelectSuggestion(_autocompleteSuggestion);
},
child: ListTile(
leading: const Icon(Icons.location_on_outlined),
title: Text(_autocompleteSuggestion.description),
),
),
))
]),
),
),
),
],
);
}
Forgive the quick code dump. 😁
You should use SingleChildScrollView on your screen where RawAutocomplete places with reverse: true property.
Just like beneath:
child: Center(
child: SingleChildScrollView(
reverse: true,
child: Column()
You could use some constraints to achieve the behavior you want.
First of all, place the root widget as a child of LayoutBuilder to get the layout constraints (I also used a Align top place the options view better).
After that, you can use a ConstrainedBox as the parent of your options view.
You can customize these constraints as you want. The example below is set to have half screen height as the max height of the options view minus the bottom view inset (dynamic as the state of the soft keyboard).
The code you gave on your example would be something like this:
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: LayoutBuilder(
builder: (context, constraints) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: RawAutocomplete<String>(
key: _autocompleteKey,
focusNode: _focusNode,
textEditingController: _textEditingController,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return _autoSuggestions;
}
return _autoSuggestions.where((dynamic option) {
return option
.toString()
.toLowerCase()
.startsWith(textEditingValue.text.toLowerCase());
});
},
optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<String> onSelected,
Iterable<String> options) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
child: Material(
elevation: 4.0,
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: constraints.biggest.width,
maxHeight: (MediaQuery.of(context).size.height / 2) -
(MediaQuery.of(context).viewInsets.bottom / 4),
),
child: ListView(
children: options
.map((String option) => GestureDetector(
onTap: () {
onSelected(option);
},
child: ListTile(
title: Text(option),
),
))
.toList(),
),
),
),
);
},
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onSubmitted,
) {
return Card(
elevation: (null == _errorText ? 8 : 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0)),
child: TextField(
controller: textEditingController,
focusNode: focusNode,
),
);
},
),
),
),
);
}

Flutter: popup menu inside popup menu

I need to achieve a list of PopUpMenu nested inside a PopUpMenu.
On click on one of the itmes I want to get another PopUpMenu with it's own items.
We could say it maight look similar to classic windows options.
Is it possible to achievie in flutter?
Sure you can:
This show two subMenu with two items each.
I used an enum for the demo:
enum Item { i1, i2, i3, i4 }
Make sure to call Navigator.pop(context) in the onSelected to close the first menu.
PopupMenuButton(
child: Text('MENU'),
itemBuilder: (BuildContext context) => <PopupMenuEntry<PopupMenuButton>>[
PopupMenuItem(
child: PopupMenuButton(
child: Text('SUBMENU A'),
onSelected: (Item result) {
setState(() { _selection = result; });
Navigator.pop(context); },
itemBuilder: (BuildContext context) => <PopupMenuEntry<Item>>[
const PopupMenuItem<Item>(
value: Item.i1,
child: Text('i1'),
),
const PopupMenuItem<Item>(
value: Item.i2,
child: Text('i2'),
),
],
),
),
PopupMenuItem(
child: PopupMenuButton(
child: Text('SUBMENU B'),
onSelected: (Item result) {
setState(() { _selection = result; });
Navigator.pop(context); },
itemBuilder: (BuildContext context) => <PopupMenuEntry<Item>>[
const PopupMenuItem<Item>(
value: Item.i3,
child: Text('i3'),
),
const PopupMenuItem<Item>(
value: Item.i4,
child: Text('i4'),
),
],
),
),
],
),
you can get nested pop up menus like this :
PopupMenuButton(
itemBuilder: (_) {
return [
PopupMenuItem(child: Text("Item1")),
PopupMenuItem(
child: PopupMenuButton(
child: Text("Nested Items"),
itemBuilder: (_) {
return [
PopupMenuItem(child: Text("Item2")),
PopupMenuItem(child: Text("Item3"))
];
},
),
),
];
},
)
You can do like this, add it to code.
class PopupMenuChildrenItem<T> extends PopupMenuEntry<T> {
const PopupMenuChildrenItem({
super.key,
this.height = kMinInteractiveDimension,
this.padding,
this.enable = true,
this.textStyle,
this.onTap,
required this.itemBuilder,
required this.child,
});
final TextStyle? textStyle;
final EdgeInsets? padding;
final bool enable;
final void Function()? onTap;
final List<PopupMenuEntry<T>> Function(BuildContext) itemBuilder;
final Widget child;
#override
final double height;
#override
bool represents(T? value) => false;
#override
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>> createState() => MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>();
}
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>> extends State<W> {
#protected
void handleTap(T value) {
widget.onTap?.call();
Navigator.pop<T>(context, value);
}
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
TextStyle style = widget.textStyle ??
popupMenuTheme.textStyle ??
theme.textTheme.subtitle1!;
return PopupMenuButton<T>(
enabled: widget.enable,
onSelected: handleTap,
itemBuilder: widget.itemBuilder,
child: AnimatedDefaultTextStyle(
style: style,
duration: kThemeChangeDuration,
child: Container(
alignment: AlignmentDirectional.centerStart,
constraints: BoxConstraints(minHeight: widget.height),
padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: 16),
child: widget.child,
),
),
);
}
}
Then, use it like
PopupMenuButton<int>(
child: const Text('MENU'),
onSelected: (result) {
setState(() {
_selection = result;
});
},
itemBuilder: (BuildContext context) => [
PopupMenuChildrenItem(
child: const Text('SUBMENU A'),
itemBuilder: (BuildContext context) => [
const PopupMenuItem(
value: 1,
child: Text('1'),
),
const PopupMenuItem(
value: 2,
child: Text('2'),
),
],
),
PopupMenuChildrenItem(
child: const Text('SUBMENU B'),
itemBuilder: (BuildContext context) => [
const PopupMenuItem(
value: 3,
child: Text('3'),
),
const PopupMenuItem(
value: 4,
child: Text('4'),
),
],
),
const PopupMenuItem(
value: 5,
child: Text('5'),
),
const PopupMenuItem(
value: 6,
child: Text('6'),
),
],
)

Deleting widget from list from a press within the widget

I have a list of custom TextFormField's that i added to them a delete icon
all I am trying to do is when I press the delete button it will be deleted from the list and the view
i tried adding a function to my form field with no success
I think my approach isn't the best way to implement what i want, I am open to any idea
here is the code
import 'package:flutter/material.dart';
typedef DeleteCallback = void Function(Key key);
class DynamicFormField extends FormField<String>{
DynamicFormField({
Key key,
FormFieldSetter<String> onSaved,
FormFieldValidator<String> validator,
String initialValue = "",
bool autovalidate = false,
DeleteCallback onDelete(Key key),
}) : super(
onSaved: onSaved,
validator: validator,
initialValue: initialValue,
autovalidate: autovalidate,
builder: (FormFieldState<String> state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
flex: 5,
child: TextFormField(
decoration: const InputDecoration(
hintText: 'Enter Player Name',
),
onSaved: onSaved,
validator: validator,
initialValue: initialValue,
autovalidate: autovalidate,
),
),
IconButton(
icon: Icon(Icons.delete_outline),
onPressed: onDelete(key)
),
],
);
}
);
}
DynamicFormField(
key: UniqueKey(),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (input) => {players.add(Player(input))},
onDelete: f,
),
);
}
void f(Key key){
fields.removeWhere((item) => item.key == key);
}
tnx
I solved it using ListView builder
import 'package:flutter/material.dart';
import 'package:rummy/models/player.dart';
import 'package:rummy/screens/game_screen.dart';
class NewGame extends StatefulWidget {
NewGame({Key key}) : super(key: key);
#override
_NewGameState createState() => _NewGameState();
}
class _NewGameState extends State<NewGame> {
final _formKey = GlobalKey<FormState>();
List<Widget> fields;
List<Player> players;
_NewGameState() {
players = new List<Player>();
fields = new List();
print(players);
fields.add(generateField());
}
Widget generateField() {
return TextFormField(
decoration: const InputDecoration(
hintText: 'Enter Player Name',
),
onSaved: (input) => {players.add(Player(input))},
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox.expand(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: <Widget>[
Form(
key: _formKey,
child: Expanded(
child: ListView(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height,
child: Builder(
builder: (BuildContext context) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: fields.length,
itemBuilder:
(BuildContext context, int postion) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: fields[postion],
),
IconButton(
icon: Icon(Icons.delete_outline),
onPressed: () => {
setState(() {
print(postion);
fields.removeAt(postion);
})
}),
],
);
},
);
},
),
)
],
),
)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
print("asdasd");
if (_formKey.currentState.validate()) {
players.clear();
_formKey.currentState.save();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GameScreen(players),
));
} else
print(_formKey.currentState.validate());
},
child: Text('Submit'),
),
RaisedButton(
onPressed: () {
setState(() {
fields.add(generateField());
});
},
child: Text('Add New Player'),
),
],
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
),
);
}
}
I used this
https://github.com/MobMaxime/Flutter-To-Do-App/blob/master/lib/screens/todo_list.dart

Add search form above Firestore list in Flutter

I am trying to render a search form above a list of items from Firestore and filter locally based on what is typed in the form.
I tried adding both widgets to the body like this, but it is only displaying the search form:
body: Column(
children: <Widget>[Searchform(), ContentWidget()],
),
This is the current code which displays a basic list:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Items extends StatefulWidget {
Items({Key key}) : super(key: key);
#override
_ItemsState createState() => _ItemsState();
}
class _ItemsState extends State<Items> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Search'),
),
body: ContentWidget(),
);
}
}
class Searchform extends StatelessWidget {
final TextEditingController _searchController = TextEditingController();
#override
Widget build(BuildContext context) {
return TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
),
);
}
}
class ContentWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('content').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['term']),
);
}).toList(),
);
}
},
);
}
}
What I was thinking of doing is saving the items in local state and filter them based on what is typed in the search box.
this is a very simple way try this code within "snapshot.data.documents.map((DocumentSnapshot document)"
if(_searchController.text.toString().contains(document['term'])){
return new ListTile(
title: new Text(document['term']),
);
}
I have provide simple filter record in listview code.
class FilterDemo extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return FilterState();
}
}
class FilterState extends State<FilterDemo> {
List<String> items, duplicateList;
TextEditingController editingController = TextEditingController();
#override
void initState() {
// TODO: implement initState
super.initState();
items = List<String>.generate(1000, (i) => "Item $i");
duplicateList = items;
}
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(duplicateList);
if (query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if (item.contains(query)) {
dummyListData.add(item);
}
});
setState(() {
items.clear();
items.addAll(dummyListData);
});
return;
} else {
setState(() {
items.clear();
items.addAll(duplicateList);
});
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Filter Demo"),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: editingController,
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(25.0)))),
),
),
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]}'),
);
},
),
),
],
),
);
}
}
I have provide Code how the saving the items in local state and filter them based on what is typed in the search box.
class UserList extends StatefulWidget {
final FirebaseUser user;
final String currentUserId;
UserList({this.currentUserId, this.user});
#override
_UserListState createState() => _UserListState();
}
class _UserListState extends State<UserList> {
TextEditingController _signUpConfirmPassword = new TextEditingController();
String _myValue = '';
UniqueKey _myKey = UniqueKey();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("UserList"),
),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: CupertinoTextField(
keyboardType: TextInputType.text,
//inputFormatters: [LengthLimitingTextInputFormatter(60)],
placeholder: 'Search For..',
// placeholderStyle: TextStyle(
// fontWeight: FontWeight.w200
// ),
prefix: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Icon(
Icons.search,
),
),
onChanged: (val) {
if (val.isNotEmpty) {
_myValue = val;
}
setState(() {
_myKey = UniqueKey();
});
},
decoration: BoxDecoration(
border: Border.all(color: primaryColor),
borderRadius: BorderRadius.circular(20.0)),
)),
SizedBox(height: 10.0),
Container(
key: _myKey,
child: FetchUsers(
user: widget.user,
myValue: _myValue,
)),
],
));
}
}
class FetchUsers extends StatefulWidget {
final String myValue;
final FirebaseUser user;
FetchUsers({this.myValue, this.user});
#override
_FetchUsersState createState() => _FetchUsersState();
}
class _FetchUsersState extends State<FetchUsers> {
List searchName = List();
List userName = List();
Future listOfUsers() {
if (widget.myValue.isEmpty) {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.orderBy('Created', descending: true)
.limit(10)
.getDocuments()
.then((d) {
userName.clear();
d.documents.forEach((f) {
userName.add(f);
});
return userName;
});
} else {
return Firestore.instance
.collection('users')
.where('Role', isEqualTo: 'user')
.limit(10)
.getDocuments()
.then((d) {
searchName.clear();
d.documents.forEach((f) {
if (f.data['Name']
.toString()
.toLowerCase()
.contains(widget.myValue.toLowerCase())) {
searchName.add(f);
}
});
return searchName;
});
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: listOfUsers(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CupertinoActivityIndicator(),
);
} else {
return ListView.separated(
physics: ClampingScrollPhysics(),
separatorBuilder: (context, int) {
return Divider();
},
itemCount: snapshot.data.length,
shrinkWrap: true,
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) {
return Card(
elevation: 7.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
' ${snapshot.data[index]['Name']}',
style: TextStyle(
color: outlineColor,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 5.0,
),
Text(
' ${snapshot.data[index]['Email']}',
),
],
),
Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => Chat(
user: widget.user,
name: snapshot.data[index]
['Name'],
peerId: snapshot.data[index]
['UID'],
)));
},
icon: Icon(
Icons.chat,
color: themeColor,
),
label: Text(
"Chat",
style: TextStyle(color: themeColor),
)),
RaisedButton.icon(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
color: primaryColor,
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) =>
SendNotificationOption(
name: snapshot.data[index]
['Name'],
myFcm: snapshot.data[index]
['UID'],
isBroadcast: false,
)));
},
icon: Icon(
Icons.notifications,
color: themeColor,
),
label: Text(
"Notification",
style: TextStyle(color: themeColor),
)),
],
),
],
),
),
));
},
);
}
},
);
}
}
What you have type in Search then that Data is shown in listview]1