Skip to content

Commit

Permalink
merge voiceSearch
Browse files Browse the repository at this point in the history
  • Loading branch information
avani17101 committed Jul 26, 2020
1 parent 4408e5e commit c302918
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 57 deletions.
32 changes: 5 additions & 27 deletions libmate/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:libmate/datastore/model.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
Expand All @@ -11,9 +10,7 @@ import 'package:libmate/views/home.dart';
import 'package:libmate/views/libcard.dart';
import 'package:libmate/views/request.dart';
import 'package:libmate/views/search.dart';
import 'package:libmate/views/issued.dart';
import 'package:fuzzy/fuzzy.dart';

import 'package:libmate/views/voiceSearch.dart';
void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
Expand All @@ -24,8 +21,6 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
bool loaded;
UserModel model;
var fuse;
List<BookModel> books;

@override
void initState() {
Expand All @@ -34,27 +29,9 @@ class _MyAppState extends State<MyApp> {
loadState();
}

void loadBookData() {
Firestore.instance.collection('books').getDocuments().then((snapshot) {
final documents = snapshot.documents;
books = List<BookModel>();

for (var document in documents) {
final name = document.data["name"];
books.add(BookModel(name: name));
}
}).then((some_res) {
final wk = WeightedKey(name: "keyer", getter: (obj) => obj.name, weight: 1);
final fo = FuzzyOptions(keys: [wk]);
fuse = Fuzzy(books, options: fo);
// in fuse.search, score of 0 is fullmatch, 1 is complete mismatch
});
}

void loadState() async {
model = await UserModel.fromSharedPrefs();

loadBookData();
print(model.uid);

setState(() {
loaded = true;
Expand Down Expand Up @@ -87,11 +64,12 @@ class _MyAppState extends State<MyApp> {
),
initialRoute: "/home",
routes: <String, WidgetBuilder>{

'/home': (BuildContext context) =>
new Home(loggedIn: usermodel.isLoggedIn()),
'/search': (BuildContext context) => new SearchPage(fuse: fuse),
'/search': (BuildContext context) => new SearchPage(),
'/voicesearch': (BuildContext context) => new VoiceSearchPage(),
'/contribute': (BuildContext context) => new ContributePage(),
'/issued': (BuildContext context) => new IssuedPage(),
'/friends': (BuildContext context) => new FriendsPage(),
'/goals': (BuildContext context) => new GoalsPage(),
'/libcard': (BuildContext context) => new LibcardPage(),
Expand Down
2 changes: 1 addition & 1 deletion libmate/lib/views/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AppDrawer extends StatelessWidget {
return <Widget>[
_DrawerViewItem(Icons.home, 'Home', '/home').build(context),
_DrawerViewItem(Icons.search, 'Search', '/search').build(context),
_DrawerViewItem(Icons.location_on, 'Guide', '/guide').build(context),
_DrawerViewItem(Icons.keyboard_voice, 'VoiceSearch', '/voicesearch').build(context),
_DrawerViewItem(Icons.file_upload, 'Contribute Info', '/contribute')
.build(context),
_DrawerViewItem(Icons.library_books, 'Issued Books', '/issued')
Expand Down
314 changes: 314 additions & 0 deletions libmate/lib/views/voiceSearch.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
import 'package:flutter/material.dart';
import 'package:libmate/views/drawer.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:english_words/english_words.dart' as words;
import 'package:speech_recognition/speech_recognition.dart';

class VoiceSearchPage extends StatefulWidget {
@override
_VoiceSearchPageState createState() => _VoiceSearchPageState();
}

class _VoiceSearchPageState extends State<VoiceSearchPage> {
SpeechRecognition _speech;
bool _speechRecognitionAvailable = false;
bool _isListening = false;

String transcription = '';

final List<String> kWords;
_SearchAppBarDelegate _searchDelegate;

//Initializing with sorted list of english words
_VoiceSearchPageState()
: kWords = List.from(Set.from(words.all))
..sort(
(w1, w2) => w1.toLowerCase().compareTo(w2.toLowerCase()),
),
super();

@override
void initState() {
super.initState();
//Initializing search delegate with sorted list of English words
_searchDelegate = _SearchAppBarDelegate(kWords);
activateSpeechRecognizer();
}

void requestPermission() async {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.microphone);

if (permission != PermissionStatus.granted) {
await PermissionHandler()
.requestPermissions([PermissionGroup.microphone]);
}
}

// Platform messages are asynchronous, so we initialize in an async method.
void activateSpeechRecognizer() {
requestPermission();

_speech = new SpeechRecognition();
_speech.setAvailabilityHandler(onSpeechAvailability);
_speech.setCurrentLocaleHandler(onCurrentLocale);
_speech.setRecognitionStartedHandler(onRecognitionStarted);
_speech.setRecognitionResultHandler(onRecognitionResult);
_speech.setRecognitionCompleteHandler(onRecognitionComplete);
_speech
.activate()
.then((res) => setState(() => _speechRecognitionAvailable = res));
}

void start() => _speech
.listen(locale: 'en_US')
.then((result) => print('Started listening => result $result'));

void cancel() =>
_speech.cancel().then((result) => setState(() => _isListening = result));

void stop() => _speech.stop().then((result) {
setState(() => _isListening = result);
});

void onSpeechAvailability(bool result) =>
setState(() => _speechRecognitionAvailable = result);

void onCurrentLocale(String locale) =>
setState(() => print("current locale: $locale"));

void onRecognitionStarted() => setState(() => _isListening = true);

void onRecognitionResult(String text) {
setState(() {
transcription = text;
showSearchPage(context, _searchDelegate, transcription);
stop();
});
}

void onRecognitionComplete() => setState(() => _isListening = false);

Widget _buildVoiceInput({String label, VoidCallback onPressed}) =>
new Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
children: <Widget>[
FlatButton(
child: Text(
label,
style: const TextStyle(color: Colors.white),
),
),
IconButton(
icon: Icon(Icons.mic),
onPressed: onPressed,
),
],
));

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Word List'),
actions: <Widget>[
_buildVoiceInput(
onPressed: _speechRecognitionAvailable && !_isListening
? () => start()
: () => stop(),
label: _isListening ? 'Listening...' : '',
),
//Adding the search widget in AppBar
IconButton(
tooltip: 'Search',
icon: const Icon(Icons.search),
//Don't block the main thread
onPressed: () {
showSearchPage(context, _searchDelegate, transcription);
},
),
],
),
drawer: new AppDrawer(),
body: Scrollbar(
//Displaying all English words in list in app's main page
child: ListView.builder(
itemCount: kWords.length,
itemBuilder: (context, idx) => ListTile(
title: Text(kWords[idx]),
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("Click the Search action"),
action: SnackBarAction(
label: 'Search',
onPressed: () {
showSearchPage(context, _searchDelegate, transcription);
},
)));
},
),
),
),
);
}

//Shows Search result
void showSearchPage(BuildContext context,
_SearchAppBarDelegate searchDelegate, String transcription) async {
final String selected = await showSearch<String>(
context: context,
delegate: searchDelegate,
query: transcription,
);

if (selected != null) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Your Word Choice: $selected'),
),
);
}
}
}

//Search delegate
class _SearchAppBarDelegate extends SearchDelegate<String> {
final List<String> _words;
final List<String> _history;
final String preQry;

_SearchAppBarDelegate(List<String> words)
: _words = words,
//pre-populated history of words
_history = <String>['apple', 'orange', 'banana', 'watermelon'],
preQry = "",
super();

// Setting leading icon for the search bar.
//Clicking on back arrow will take control to main page
@override
Widget buildLeading(BuildContext context) {
return IconButton(
tooltip: 'Back',
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
//Take control back to previous page
this.close(context, null);
},
);
}

// Builds page to populate search results.
@override
Widget buildResults(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('===Your Word Choice==='),
GestureDetector(
onTap: () {
//Define your action when clicking on result item.
//In this example, it simply closes the page
this.close(context, this.query);
},
child: Text(
this.query,
style: Theme.of(context)
.textTheme
.display2
.copyWith(fontWeight: FontWeight.normal),
),
),
],
),
),
);
}

// Suggestions list while typing search query - this.query.
@override
Widget buildSuggestions(BuildContext context) {
final Iterable<String> suggestions = this.query.isEmpty
? _history
: _words.where((word) => word.startsWith(query));

return _WordSuggestionList(
query: this.query,
suggestions: suggestions.toList(),
onSelected: (String suggestion) {
this.query = suggestion;
this._history.insert(0, suggestion);
showResults(context);
},
);
}

// Action buttons at the right of search bar.
@override
List<Widget> buildActions(BuildContext context) {
List<Widget> actions = List();
if (query.isNotEmpty) {
actions.add(IconButton(
tooltip: 'Clear',
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
showSuggestions(context);
},
));
}

return actions;
}
}

// Suggestions list widget displayed in the search page.
class _WordSuggestionList extends StatelessWidget {
const _WordSuggestionList({this.suggestions, this.query, this.onSelected});

final List<String> suggestions;
final String query;
final ValueChanged<String> onSelected;

@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme.subhead;
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (BuildContext context, int i) {
final String suggestion = suggestions[i];
return ListTile(
leading: query.isEmpty ? Icon(Icons.history) : Icon(null),
// Highlight the substring that matched the query.
title: RichText(
text: TextSpan(
text: suggestion.substring(0, query.length),
style: textTheme.copyWith(fontWeight: FontWeight.bold),
children: <TextSpan>[
TextSpan(
text: suggestion.substring(query.length),
style: textTheme,
),
],
),
),
onTap: () {
onSelected(suggestion);
},
);
},
);
}
}



Loading

0 comments on commit c302918

Please sign in to comment.