Modified the search functionality so teh resulting list updates

as the user types. Added a clearing button to the search box.
This commit is contained in:
Mariano Uvalle 2019-04-05 20:29:59 -06:00
parent d59e7368c5
commit 47c7c4cef9
2 changed files with 95 additions and 43 deletions

View file

@ -25,14 +25,27 @@ class HomeBloc {
/// A subject of list of task model.
final _tasks = BehaviorSubject<List<TaskModel>>();
/// Current text from the search box.
String _searchBoxText = '';
/// A subject of the search box updates.
final _searchBoxText = BehaviorSubject<String>(seedValue: '');
// Stream getters.
// The result has to be a combination of the tasks streams and the text
// stream, because otherwise, the tasks stream has no way of knowing when
// there's a new update in the text.
//
// The search box transformation has to be applied after the combination,
// otherwhise the value used for filtering is outdated and the list output is
// not synchronized with the current value of the searhc box text.
/// An observalbe of the taks of a user.
Observable<List<TaskModel>> get userTasks => _tasks.stream
.transform(prioritySortTransformer())
.transform(searchBoxTransformer());
Observable<List<TaskModel>> get userTasks =>
Observable.combineLatest2<String, List<TaskModel>, List<TaskModel>>(
_searchBoxText.stream,
_tasks.stream.transform(prioritySortTransformer()),
(text, tasks) {
return tasks;
},
).transform(searchBoxTransformer());
/// An observable of the current logged in user.
Observable<FirebaseUser> get userStream => _auth.userStream;
@ -55,17 +68,17 @@ class HomeBloc {
sink.add(
taskList.where(
(TaskModel task) {
if (_searchBoxText == '') {
if (_searchBoxText.value == '') {
return true;
}
// Return true if the text in the search box matches the title
// or the text of the task.
return task.event
.toLowerCase()
.contains(_searchBoxText.toLowerCase()) ||
.contains(_searchBoxText.value.toLowerCase()) ||
task.text
.toLowerCase()
.contains(_searchBoxText.toLowerCase());
.contains(_searchBoxText.value.toLowerCase());
},
).toList(),
);
@ -106,10 +119,11 @@ class HomeBloc {
/// Updates the serach box text.
void updateSearchBoxText(String newText) {
_searchBoxText = newText;
_searchBoxText.add(newText);
}
void dispose() {
_searchBoxText.close();
_tasks.close();
}
}

View file

@ -6,27 +6,87 @@ import './gradient_touchable_container.dart';
//TODO: Add neccessary properties to be able to inform of changes in text field.
/// A search box that mathces the app mocks.
class SearchBox extends StatelessWidget {
class SearchBox extends StatefulWidget {
/// Height of the sarch box.
final double height;
/// Function to be called when the text changes.
final Function(String) onChanged;
/// Creates a search box.
///
/// the height should be equal or larger than 50.
SearchBox({
@required this.height,
@required this.onChanged,
}) : assert(height >= 50);
@override
_SearchBoxState createState() => _SearchBoxState();
}
class _SearchBoxState extends State<SearchBox> {
/// Controller for the [TextFiel].
final TextEditingController _controller = TextEditingController();
initState() {
_controller.addListener(() => widget.onChanged(_controller.text));
super.initState();
}
Widget build(BuildContext context) {
final List<Widget> containerRowChildren = <Widget>[
SizedBox(
width: 10,
),
Icon(
FontAwesomeIcons.sistrix,
color: Colors.white,
),
SizedBox(
width: 8,
),
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search...',
hintStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
cursorColor: Colors.white,
scrollPadding: EdgeInsets.zero,
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
];
if (_controller.text != '') {
containerRowChildren.add(
IconButton(
icon: Icon(
FontAwesomeIcons.timesCircle,
color: Colors.white,
),
onPressed: onClearButtonPressed,
),
);
}
return Row(
children: <Widget>[
Spacer(flex: 1),
Expanded(
flex: 8,
child: GradientTouchableContainer(
radius: height / 2,
height: height,
radius: widget.height / 2,
height: widget.height,
shadow: BoxShadow(
color: Color(0x20FFFFFF),
offset: Offset(0, 3),
@ -34,37 +94,7 @@ class SearchBox extends StatelessWidget {
spreadRadius: 1,
),
child: Row(
children: <Widget>[
SizedBox(
width: 10,
),
Icon(
FontAwesomeIcons.sistrix,
color: Colors.white,
),
SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: onChanged,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search...',
hintStyle: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
cursorColor: Colors.white,
scrollPadding: EdgeInsets.zero,
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
],
children: containerRowChildren,
),
),
),
@ -72,4 +102,12 @@ class SearchBox extends StatelessWidget {
],
);
}
void onClearButtonPressed() {
_controller.text = '';
// The controller does not notify its listeners when the text is set
// explicitely. We have to do it manually.
widget.onChanged(_controller.text);
setState(() {});
}
}