react-native-voice-ts 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +914 -83
  2. package/dist/components/MicIcon.d.ts +23 -0
  3. package/dist/components/MicIcon.d.ts.map +1 -0
  4. package/dist/components/MicIcon.js +28 -0
  5. package/dist/components/MicIcon.js.map +1 -0
  6. package/dist/components/VoiceMicrophone.d.ts +93 -0
  7. package/dist/components/VoiceMicrophone.d.ts.map +1 -0
  8. package/dist/components/VoiceMicrophone.js +239 -0
  9. package/dist/components/VoiceMicrophone.js.map +1 -0
  10. package/dist/components/index.d.ts +5 -0
  11. package/dist/components/index.d.ts.map +1 -0
  12. package/dist/components/index.js +3 -0
  13. package/dist/components/index.js.map +1 -0
  14. package/dist/hooks/index.d.ts +3 -0
  15. package/dist/hooks/index.d.ts.map +1 -0
  16. package/dist/hooks/index.js +2 -0
  17. package/dist/hooks/index.js.map +1 -0
  18. package/dist/hooks/useVoiceRecognition.d.ts +89 -0
  19. package/dist/hooks/useVoiceRecognition.d.ts.map +1 -0
  20. package/dist/hooks/useVoiceRecognition.js +227 -0
  21. package/dist/hooks/useVoiceRecognition.js.map +1 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -0
  25. package/dist/index.js.map +1 -1
  26. package/ios/Voice.xcodeproj/project.xcworkspace/xcuserdata/olumayowadaniel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  27. package/ios/Voice.xcodeproj/project.xcworkspace/xcuserdata/rudie_shahinian.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  28. package/package.json +25 -7
  29. package/plugin/src/withVoice.ts +74 -0
  30. package/plugin/tsconfig.json +10 -0
  31. package/src/components/MicIcon.tsx +72 -0
  32. package/src/components/VoiceMicrophone.tsx +345 -0
  33. package/src/components/index.ts +4 -0
  34. package/src/hooks/index.ts +5 -0
  35. package/src/hooks/useVoiceRecognition.ts +333 -0
  36. package/src/images/mic.svg +16 -0
  37. package/src/index.ts +15 -0
  38. package/.nvmrc +0 -1
  39. package/.prettierrc +0 -5
  40. package/.releaserc +0 -15
  41. package/MIGRATION_SUMMARY.md +0 -510
package/README.md CHANGED
@@ -15,6 +15,7 @@ A powerful, production-ready speech-to-text library for React Native application
15
15
  [Features](#-features) •
16
16
  [Installation](#-installation) •
17
17
  [Quick Start](#-quick-start) •
18
+ [Component Usage](#-component--hook-usage) •
18
19
  [API Reference](#-api-reference) •
19
20
  [Examples](#-complete-examples) •
20
21
  [Contributing](#-contributing)
@@ -23,7 +24,7 @@ A powerful, production-ready speech-to-text library for React Native application
23
24
 
24
25
  ---
25
26
 
26
- ## ✨ Features
27
+ ### ✨ Features
27
28
 
28
29
  ### Core Capabilities
29
30
 
@@ -43,6 +44,9 @@ A powerful, production-ready speech-to-text library for React Native application
43
44
  - 🎨 **Modern API** - Clean, intuitive API design
44
45
  - 🛡️ **Error Handling** - Comprehensive error management
45
46
  - 🔧 **Fully Customizable** - Extensive configuration options
47
+ - 🎪 **Ready-to-use Components** - VoiceMicrophone component & useVoiceRecognition hook
48
+ - 🔌 **Plug & Play** - Import and use instantly in any React Native app
49
+ - 🎯 **SVG Icons Included** - Beautiful Lucide-based mic icons (MicIcon, MicOffIcon)
46
50
 
47
51
  ### Platform Support
48
52
 
@@ -98,56 +102,168 @@ cd ios && pod install && cd ..
98
102
 
99
103
  ## 🚀 Quick Start
100
104
 
101
- ### Basic Usage
105
+ ### Three Ways to Use
102
106
 
103
107
  ```typescript
104
- import React, { useEffect, useState } from 'react';
105
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
106
- import Voice from '@react-native-voice/voice';
108
+ // 1. Ready-to-use Component (Easiest)
109
+ import { VoiceMicrophone } from 'react-native-voice-ts';
107
110
 
108
- const App = () => {
109
- const [isRecording, setIsRecording] = useState(false);
110
- const [recognizedText, setRecognizedText] = useState('');
111
+ // 2. Custom Hook (More control)
112
+ import { useVoiceRecognition } from 'react-native-voice-ts';
111
113
 
112
- useEffect(() => {
113
- // Set up event listeners
114
- Voice.onSpeechResults = (e) => {
115
- setRecognizedText(e.value[0]);
116
- };
117
-
118
- // Cleanup
119
- return () => {
120
- Voice.destroy().then(Voice.removeAllListeners);
121
- };
122
- }, []);
114
+ // 3. Core API (Advanced)
115
+ import Voice from 'react-native-voice-ts';
123
116
 
124
- const startRecording = async () => {
125
- try {
126
- await Voice.start('en-US');
127
- setIsRecording(true);
128
- } catch (error) {
129
- console.error(error);
130
- }
131
- };
117
+ // 4. Import SVG Icons (Optional - for custom UI)
118
+ import { MicIcon, MicOffIcon } from 'react-native-voice-ts';
119
+ ```
132
120
 
133
- const stopRecording = async () => {
134
- try {
135
- await Voice.stop();
136
- setIsRecording(false);
137
- } catch (error) {
138
- console.error(error);
139
- }
140
- };
121
+ **Note:** To use SVG icons, install `react-native-svg`:
122
+
123
+ ```bash
124
+ npm install react-native-svg
125
+ # or
126
+ yarn add react-native-svg
127
+ ```
128
+
129
+ ---
130
+
131
+ ### ⚡ Super Simple - Just Import and Use!
132
+
133
+ **Example 1: Voice Search (Minimal Code)**
134
+
135
+ ```tsx
136
+ import React, { useState } from 'react';
137
+ import { View, TextInput, Button } from 'react-native';
138
+ import { VoiceMicrophone } from 'react-native-voice-ts';
139
+
140
+ export default function App() {
141
+ const [searchText, setSearchText] = useState('');
142
+
143
+ return (
144
+ <View style={{ padding: 20 }}>
145
+ <TextInput
146
+ value={searchText}
147
+ onChangeText={setSearchText}
148
+ placeholder="Type or speak..."
149
+ />
150
+
151
+ <VoiceMicrophone onSpeechResult={setSearchText}>
152
+ {({ isRecording, start, stop }) => (
153
+ <Button
154
+ onPress={isRecording ? stop : start}
155
+ title={isRecording ? '⏹ Stop' : '🎤 Speak'}
156
+ />
157
+ )}
158
+ </VoiceMicrophone>
159
+ </View>
160
+ );
161
+ }
162
+ ```
163
+
164
+ **Example 2: With Custom Styling & SVG Icons**
165
+
166
+ ```tsx
167
+ import React, { useState } from 'react';
168
+ import { View, TextInput, TouchableOpacity, StyleSheet } from 'react-native';
169
+ import { VoiceMicrophone, MicIcon, MicOffIcon } from 'react-native-voice-ts';
170
+
171
+ export default function App() {
172
+ const [text, setText] = useState('');
141
173
 
142
174
  return (
143
175
  <View style={styles.container}>
144
- <Text style={styles.text}>{recognizedText || 'Press to speak'}</Text>
145
- <TouchableOpacity
146
- style={[styles.button, isRecording && styles.recording]}
147
- onPress={isRecording ? stopRecording : startRecording}
148
- >
149
- <Text style={styles.buttonText}>
150
- {isRecording ? '🔴 Stop' : '🎤 Start'}
176
+ <TextInput
177
+ style={styles.input}
178
+ value={text}
179
+ onChangeText={setText}
180
+ placeholder="Search..."
181
+ />
182
+
183
+ <VoiceMicrophone onSpeechResult={setText}>
184
+ {({ isRecording, start, stop }) => (
185
+ <TouchableOpacity
186
+ style={[styles.mic, isRecording && styles.recording]}
187
+ onPress={isRecording ? stop : start}
188
+ >
189
+ {isRecording ? (
190
+ <MicOffIcon size={24} color="#fff" />
191
+ ) : (
192
+ <MicIcon size={24} color="#fff" />
193
+ )}
194
+ </TouchableOpacity>
195
+ )}
196
+ </VoiceMicrophone>
197
+ </View>
198
+ );
199
+ }
200
+
201
+ const styles = StyleSheet.create({
202
+ container: { flexDirection: 'row', padding: 20 },
203
+ input: { flex: 1, borderWidth: 1, padding: 10, marginRight: 10 },
204
+ mic: {
205
+ width: 50,
206
+ height: 50,
207
+ borderRadius: 25,
208
+ backgroundColor: '#007AFF',
209
+ justifyContent: 'center',
210
+ alignItems: 'center',
211
+ },
212
+ recording: { backgroundColor: '#FF3B30' },
213
+ });
214
+ ```
215
+
216
+ ---
217
+
218
+ ### 🎯 Using Hook (For More Control)
219
+
220
+ ```tsx
221
+ import React from 'react';
222
+ import { View, Text, Button } from 'react-native';
223
+ import { useVoiceRecognition } from 'react-native-voice-ts';
224
+
225
+ export default function App() {
226
+ const { isRecording, results, start, stop } = useVoiceRecognition({
227
+ onResult: (text) => console.log('You said:', text),
228
+ });
229
+
230
+ return (
231
+ <View style={{ padding: 20 }}>
232
+ <Text>{results[0] || 'Press to speak'}</Text>
233
+ <Button
234
+ onPress={isRecording ? stop : start}
235
+ title={isRecording ? 'Stop' : 'Start'}
236
+ />
237
+ </View>
238
+ );
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ### 🔧 Advanced (Core API)
245
+
246
+ ```tsx
247
+ import React, { useEffect, useState } from 'react';
248
+ import { View, Text, Button } from 'react-native';
249
+ import Voice from 'react-native-voice-ts';
250
+
251
+ export default function App() {
252
+ const [text, setText] = useState('');
253
+
254
+ useEffect(() => {
255
+ Voice.onSpeechResults = (e) => setText(e.value[0]);
256
+ return () => Voice.destroy().then(Voice.removeAllListeners);
257
+ }, []);
258
+
259
+ return (
260
+ <View style={{ padding: 20 }}>
261
+ <Text>{text}</Text>
262
+ <Button onPress={() => Voice.start('en-US')} title="Start" />
263
+ <Button onPress={() => Voice.stop()} title="Stop" />
264
+ </View>
265
+ );
266
+ }
151
267
  </Text>
152
268
  </TouchableOpacity>
153
269
  </View>
@@ -192,7 +308,95 @@ export default App;
192
308
 
193
309
  ## 📚 API Reference
194
310
 
195
- ### Methods
311
+ ### Components
312
+
313
+ #### VoiceMicrophone
314
+
315
+ A ready-to-use React component that handles all voice recognition logic.
316
+
317
+ **Props:**
318
+
319
+ | Prop | Type | Default | Description |
320
+ | ---------------------- | ------------------------- | --------- | ----------------------------------------------------- |
321
+ | `onSpeechResult` | `(text: string) => void` | - | Callback when final speech result is available |
322
+ | `onPartialResult` | `(text: string) => void` | - | Callback when partial (real-time) result is available |
323
+ | `onStart` | `() => void` | - | Callback when recording starts |
324
+ | `onStop` | `() => void` | - | Callback when recording stops |
325
+ | `onError` | `(error: string) => void` | - | Callback when an error occurs |
326
+ | `locale` | `string` | `'en-US'` | Language locale for recognition |
327
+ | `autoStart` | `boolean` | `false` | Auto-start recording on mount |
328
+ | `enablePartialResults` | `boolean` | `true` | Enable real-time partial results |
329
+ | `children` | `function` | - | Render prop function |
330
+
331
+ **Children Render Props:**
332
+
333
+ ```typescript
334
+ {
335
+ isRecording: boolean; // Whether recording is active
336
+ recognizedText: string; // Final recognized text
337
+ partialText: string; // Real-time partial text
338
+ start: () => Promise<void>; // Start recording
339
+ stop: () => Promise<void>; // Stop recording
340
+ cancel: () => Promise<void>; // Cancel recording
341
+ error: string | null; // Error message if any
342
+ }
343
+ ```
344
+
345
+ **Example:**
346
+
347
+ ```typescript
348
+ <VoiceMicrophone
349
+ locale="en-US"
350
+ onSpeechResult={(text) => console.log(text)}
351
+ >
352
+ {({ isRecording, start, stop }) => (
353
+ <Button onPress={isRecording ? stop : start} />
354
+ )}
355
+ </VoiceMicrophone>
356
+ ```
357
+
358
+ ### Hooks
359
+
360
+ #### useVoiceRecognition
361
+
362
+ A custom hook that provides voice recognition functionality.
363
+
364
+ **Options:**
365
+
366
+ | Option | Type | Default | Description |
367
+ | ---------------------- | ------------------------- | --------- | ---------------------- |
368
+ | `locale` | `string` | `'en-US'` | Language locale |
369
+ | `enablePartialResults` | `boolean` | `true` | Enable partial results |
370
+ | `onResult` | `(text: string) => void` | - | Result callback |
371
+ | `onError` | `(error: string) => void` | - | Error callback |
372
+
373
+ **Returns:**
374
+
375
+ ```typescript
376
+ {
377
+ isRecording: boolean;
378
+ results: string[];
379
+ partialResults: string[];
380
+ error: string | null;
381
+ start: () => Promise<void>;
382
+ stop: () => Promise<void>;
383
+ cancel: () => Promise<void>;
384
+ reset: () => void;
385
+ }
386
+ ```
387
+
388
+ **Example:**
389
+
390
+ ```typescript
391
+ const { isRecording, results, start, stop } = useVoiceRecognition({
392
+ locale: 'en-US',
393
+ onResult: (text) => setSearchQuery(text),
394
+ });
395
+ ```
396
+
397
+ ---
398
+
399
+ ### Core API Methods
196
400
 
197
401
  #### Core Methods
198
402
 
@@ -425,61 +629,688 @@ import type {
425
629
 
426
630
  ---
427
631
 
428
- ## 💡 Complete Examples
632
+ ## 💡 More Examples
633
+
634
+ ### Example 1: Simple Voice Search
635
+
636
+ ```tsx
637
+ import React, { useState } from 'react';
638
+ import { View, TextInput, Button } from 'react-native';
639
+ import { VoiceMicrophone } from 'react-native-voice-ts';
640
+
641
+ function VoiceSearch() {
642
+ const [query, setQuery] = useState('');
643
+
644
+ return (
645
+ <View>
646
+ <TextInput
647
+ value={query}
648
+ onChangeText={setQuery}
649
+ placeholder="Search..."
650
+ />
651
+ <VoiceMicrophone onSpeechResult={setQuery}>
652
+ {({ isRecording, start, stop }) => (
653
+ <Button onPress={isRecording ? stop : start} title="🎤" />
654
+ )}
655
+ </VoiceMicrophone>
656
+ </View>
657
+ );
658
+ }
659
+ ```
660
+
661
+ ### Example 2: Real-time Transcription
662
+
663
+ ```tsx
664
+ import React, { useState } from 'react';
665
+ import { View, Text, Button } from 'react-native';
666
+ import { VoiceMicrophone } from 'react-native-voice-ts';
667
+
668
+ function LiveTranscription() {
669
+ const [text, setText] = useState('');
670
+
671
+ return (
672
+ <View>
673
+ <VoiceMicrophone
674
+ enablePartialResults
675
+ onSpeechResult={setText}
676
+ onPartialResult={(live) => console.log('Live:', live)}
677
+ >
678
+ {({ isRecording, partialText, start, stop }) => (
679
+ <>
680
+ <Text>{isRecording ? partialText : text}</Text>
681
+ <Button
682
+ onPress={isRecording ? stop : start}
683
+ title={isRecording ? 'Stop' : 'Start'}
684
+ />
685
+ </>
686
+ )}
687
+ </VoiceMicrophone>
688
+ </View>
689
+ );
690
+ }
691
+ ```
692
+
693
+ ### Example 3: Multi-Language
694
+
695
+ ```tsx
696
+ import React, { useState } from 'react';
697
+ import { View, Text, Button, Picker } from 'react-native';
698
+ import { VoiceMicrophone } from 'react-native-voice-ts';
699
+
700
+ function MultiLanguage() {
701
+ const [lang, setLang] = useState('en-US');
702
+ const [text, setText] = useState('');
703
+
704
+ return (
705
+ <View>
706
+ <Picker selectedValue={lang} onValueChange={setLang}>
707
+ <Picker.Item label="English" value="en-US" />
708
+ <Picker.Item label="Spanish" value="es-ES" />
709
+ <Picker.Item label="French" value="fr-FR" />
710
+ </Picker>
711
+
712
+ <Text>{text}</Text>
713
+
714
+ <VoiceMicrophone locale={lang} onSpeechResult={setText}>
715
+ {({ isRecording, start, stop }) => (
716
+ <Button onPress={isRecording ? stop : start} title="Speak" />
717
+ )}
718
+ </VoiceMicrophone>
719
+ </View>
720
+ );
721
+ }
722
+ ```
723
+
724
+ ### Example 4: Voice Form
725
+
726
+ ```tsx
727
+ import React, { useState } from 'react';
728
+ import { View, TextInput, Button } from 'react-native';
729
+ import { VoiceMicrophone } from 'react-native-voice-ts';
730
+
731
+ function VoiceForm() {
732
+ const [name, setName] = useState('');
733
+ const [email, setEmail] = useState('');
734
+ const [active, setActive] = useState(null);
735
+
736
+ return (
737
+ <View>
738
+ <TextInput value={name} onChangeText={setName} placeholder="Name" />
739
+ <VoiceMicrophone onSpeechResult={setName}>
740
+ {({ isRecording, start, stop }) => (
741
+ <Button onPress={isRecording ? stop : start} title="🎤" />
742
+ )}
743
+ </VoiceMicrophone>
744
+
745
+ <TextInput value={email} onChangeText={setEmail} placeholder="Email" />
746
+ <VoiceMicrophone onSpeechResult={setEmail}>
747
+ {({ isRecording, start, stop }) => (
748
+ <Button onPress={isRecording ? stop : start} title="🎤" />
749
+ )}
750
+ </VoiceMicrophone>
751
+ </View>
752
+ );
753
+ }
754
+ ```
755
+
756
+ ### Example 5: Using the Hook
757
+
758
+ ```tsx
759
+ import React from 'react';
760
+ import { View, Text, Button } from 'react-native';
761
+ import { useVoiceRecognition } from 'react-native-voice-ts';
762
+
763
+ function HookExample() {
764
+ const { isRecording, results, partialResults, start, stop, reset } =
765
+ useVoiceRecognition({
766
+ locale: 'en-US',
767
+ onResult: (text) => console.log(text),
768
+ });
769
+
770
+ return (
771
+ <View>
772
+ <Text>{isRecording ? partialResults[0] : results[0]}</Text>
773
+ <Button
774
+ onPress={isRecording ? stop : start}
775
+ title={isRecording ? 'Stop' : 'Start'}
776
+ />
777
+ <Button onPress={reset} title="Clear" />
778
+ </View>
779
+ );
780
+ }
781
+ ```
782
+
783
+ **More examples in the repo:**
784
+
785
+ - [VoiceSearchExample.tsx](./example/src/VoiceSearchExample.tsx) - Full search bar implementation
786
+ - [VoiceHookExample.tsx](./example/src/VoiceHookExample.tsx) - Hook usage with advanced features
787
+ - [COMPONENT_USAGE.md](./COMPONENT_USAGE.md) - Comprehensive component guide
788
+ style={[styles.button, isRecording && styles.recording]}
789
+ onPress={isRecording ? stop : start} >
790
+ <Text style={styles.buttonText}>
791
+ {isRecording ? '⏹ Stop' : '🎤 Start Recording'}
792
+ </Text>
793
+ </TouchableOpacity>
794
+ {error && <Text style={styles.error}>{error}</Text>}
795
+ </View>
796
+ )}
797
+ </VoiceMicrophone>
798
+ </View>
799
+ );
800
+ };
801
+
802
+ const styles = StyleSheet.create({
803
+ container: {
804
+ flex: 1,
805
+ padding: 20,
806
+ backgroundColor: '#f5f5f5',
807
+ justifyContent: 'center',
808
+ },
809
+ title: {
810
+ fontSize: 24,
811
+ fontWeight: 'bold',
812
+ textAlign: 'center',
813
+ marginBottom: 30,
814
+ },
815
+ textContainer: {
816
+ backgroundColor: '#fff',
817
+ padding: 20,
818
+ borderRadius: 10,
819
+ marginBottom: 30,
820
+ elevation: 2,
821
+ },
822
+ label: {
823
+ fontSize: 12,
824
+ color: '#666',
825
+ marginTop: 10,
826
+ marginBottom: 5,
827
+ fontWeight: '600',
828
+ },
829
+ liveText: {
830
+ fontSize: 16,
831
+ color: '#007AFF',
832
+ fontStyle: 'italic',
833
+ },
834
+ finalText: {
835
+ fontSize: 18,
836
+ color: '#000',
837
+ fontWeight: '500',
838
+ },
839
+ button: {
840
+ backgroundColor: '#007AFF',
841
+ padding: 15,
842
+ borderRadius: 25,
843
+ alignItems: 'center',
844
+ },
845
+ recording: {
846
+ backgroundColor: '#FF3B30',
847
+ },
848
+ buttonText: {
849
+ color: '#fff',
850
+ fontSize: 18,
851
+ fontWeight: 'bold',
852
+ },
853
+ error: {
854
+ color: '#FF3B30',
855
+ textAlign: 'center',
856
+ marginTop: 10,
857
+ },
858
+ });
859
+
860
+ ````
861
+
862
+ ### Example 3: Multi-language Voice Input
429
863
 
430
- For comprehensive examples including:
864
+ ```typescript
865
+ import React, { useState } from 'react';
866
+ import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from 'react-native';
867
+ import { VoiceMicrophone } from 'react-native-voice-ts';
868
+
869
+ const MultiLanguageVoice = () => {
870
+ const [selectedLanguage, setSelectedLanguage] = useState('en-US');
871
+ const [text, setText] = useState('');
872
+
873
+ const languages = [
874
+ { code: 'en-US', name: 'English (US)', flag: '🇺🇸' },
875
+ { code: 'es-ES', name: 'Spanish', flag: '🇪🇸' },
876
+ { code: 'fr-FR', name: 'French', flag: '🇫🇷' },
877
+ { code: 'de-DE', name: 'German', flag: '🇩🇪' },
878
+ { code: 'zh-CN', name: 'Chinese', flag: '🇨🇳' },
879
+ { code: 'ja-JP', name: 'Japanese', flag: '🇯🇵' },
880
+ ];
431
881
 
432
- - Simple Voice-to-Text
433
- - Real-time Transcription with Partial Results
434
- - Multi-language Support
435
- - Voice Commands
436
- - Permission Handling
437
- - Custom Hooks
882
+ return (
883
+ <View style={styles.container}>
884
+ <Text style={styles.title}>Multi-language Voice Input</Text>
885
+
886
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.langScroll}>
887
+ {languages.map((lang) => (
888
+ <TouchableOpacity
889
+ key={lang.code}
890
+ style={[
891
+ styles.langButton,
892
+ selectedLanguage === lang.code && styles.selectedLang,
893
+ ]}
894
+ onPress={() => setSelectedLanguage(lang.code)}
895
+ >
896
+ <Text style={styles.flag}>{lang.flag}</Text>
897
+ <Text style={styles.langName}>{lang.name}</Text>
898
+ </TouchableOpacity>
899
+ ))}
900
+ </ScrollView>
901
+
902
+ <View style={styles.resultContainer}>
903
+ <Text style={styles.resultText}>{text || 'Select language and speak...'}</Text>
904
+ </View>
905
+
906
+ <VoiceMicrophone
907
+ locale={selectedLanguage}
908
+ onSpeechResult={setText}
909
+ >
910
+ {({ isRecording, start, stop }) => (
911
+ <TouchableOpacity
912
+ style={[styles.micButton, isRecording && styles.recording]}
913
+ onPress={isRecording ? stop : start}
914
+ >
915
+ <Text style={styles.micIcon}>{isRecording ? '⏹' : '🎤'}</Text>
916
+ <Text style={styles.micText}>
917
+ {isRecording ? 'Stop' : 'Start Recording'}
918
+ </Text>
919
+ </TouchableOpacity>
920
+ )}
921
+ </VoiceMicrophone>
922
+ </View>
923
+ );
924
+ };
438
925
 
439
- Please refer to the [example](./example) directory in this repository.
926
+ const styles = StyleSheet.create({
927
+ container: {
928
+ flex: 1,
929
+ padding: 20,
930
+ backgroundColor: '#fff',
931
+ },
932
+ title: {
933
+ fontSize: 24,
934
+ fontWeight: 'bold',
935
+ marginBottom: 20,
936
+ textAlign: 'center',
937
+ },
938
+ langScroll: {
939
+ marginBottom: 20,
940
+ },
941
+ langButton: {
942
+ padding: 10,
943
+ marginRight: 10,
944
+ borderRadius: 10,
945
+ backgroundColor: '#f0f0f0',
946
+ alignItems: 'center',
947
+ minWidth: 100,
948
+ },
949
+ selectedLang: {
950
+ backgroundColor: '#007AFF',
951
+ },
952
+ flag: {
953
+ fontSize: 30,
954
+ marginBottom: 5,
955
+ },
956
+ langName: {
957
+ fontSize: 12,
958
+ },
959
+ resultContainer: {
960
+ flex: 1,
961
+ justifyContent: 'center',
962
+ alignItems: 'center',
963
+ backgroundColor: '#f9f9f9',
964
+ borderRadius: 10,
965
+ padding: 20,
966
+ marginBottom: 20,
967
+ },
968
+ resultText: {
969
+ fontSize: 18,
970
+ textAlign: 'center',
971
+ },
972
+ micButton: {
973
+ backgroundColor: '#007AFF',
974
+ padding: 20,
975
+ borderRadius: 50,
976
+ alignItems: 'center',
977
+ },
978
+ recording: {
979
+ backgroundColor: '#FF3B30',
980
+ },
981
+ micIcon: {
982
+ fontSize: 40,
983
+ },
984
+ micText: {
985
+ color: '#fff',
986
+ marginTop: 10,
987
+ fontSize: 16,
988
+ fontWeight: 'bold',
989
+ },
990
+ });
991
+ ````
440
992
 
441
- ### Quick Example: Custom Hook
993
+ ### Example 4: Using Custom Hook with Form Input
442
994
 
443
995
  ```typescript
444
- import { useEffect, useState, useCallback } from 'react';
445
- import Voice from '@react-native-voice/voice';
996
+ import React, { useState } from 'react';
997
+ import { View, TextInput, TouchableOpacity, Text, StyleSheet } from 'react-native';
998
+ import { useVoiceRecognition } from 'react-native-voice-ts';
999
+
1000
+ const VoiceForm = () => {
1001
+ const [name, setName] = useState('');
1002
+ const [message, setMessage] = useState('');
1003
+ const [activeField, setActiveField] = useState<'name' | 'message' | null>(null);
1004
+
1005
+ const { isRecording, results, start, stop } = useVoiceRecognition({
1006
+ locale: 'en-US',
1007
+ onResult: (text) => {
1008
+ if (activeField === 'name') {
1009
+ setName(text);
1010
+ } else if (activeField === 'message') {
1011
+ setMessage(text);
1012
+ }
1013
+ setActiveField(null);
1014
+ },
1015
+ });
1016
+
1017
+ const startRecordingForField = (field: 'name' | 'message') => {
1018
+ setActiveField(field);
1019
+ start();
1020
+ };
446
1021
 
447
- export const useVoiceRecognition = (locale = 'en-US') => {
448
- const [isRecording, setIsRecording] = useState(false);
449
- const [results, setResults] = useState<string[]>([]);
450
- const [error, setError] = useState<string | null>(null);
1022
+ return (
1023
+ <View style={styles.container}>
1024
+ <Text style={styles.title}>Voice Form</Text>
1025
+
1026
+ <View style={styles.inputGroup}>
1027
+ <Text style={styles.label}>Name:</Text>
1028
+ <View style={styles.inputRow}>
1029
+ <TextInput
1030
+ style={styles.input}
1031
+ value={name}
1032
+ onChangeText={setName}
1033
+ placeholder="Your name"
1034
+ />
1035
+ <TouchableOpacity
1036
+ style={[
1037
+ styles.micBtn,
1038
+ isRecording && activeField === 'name' && styles.recording,
1039
+ ]}
1040
+ onPress={() =>
1041
+ isRecording ? stop() : startRecordingForField('name')
1042
+ }
1043
+ >
1044
+ <Text>🎤</Text>
1045
+ </TouchableOpacity>
1046
+ </View>
1047
+ </View>
1048
+
1049
+ <View style={styles.inputGroup}>
1050
+ <Text style={styles.label}>Message:</Text>
1051
+ <View style={styles.inputRow}>
1052
+ <TextInput
1053
+ style={[styles.input, styles.textArea]}
1054
+ value={message}
1055
+ onChangeText={setMessage}
1056
+ placeholder="Your message"
1057
+ multiline
1058
+ />
1059
+ <TouchableOpacity
1060
+ style={[
1061
+ styles.micBtn,
1062
+ isRecording && activeField === 'message' && styles.recording,
1063
+ ]}
1064
+ onPress={() =>
1065
+ isRecording ? stop() : startRecordingForField('message')
1066
+ }
1067
+ >
1068
+ <Text>🎤</Text>
1069
+ </TouchableOpacity>
1070
+ </View>
1071
+ </View>
451
1072
 
452
- useEffect(() => {
453
- Voice.onSpeechStart = () => setIsRecording(true);
454
- Voice.onSpeechEnd = () => setIsRecording(false);
455
- Voice.onSpeechResults = (e) => setResults(e.value || []);
456
- Voice.onSpeechError = (e) => setError(e.error?.message || 'Error');
457
-
458
- return () => {
459
- Voice.destroy().then(Voice.removeAllListeners);
460
- };
461
- }, []);
1073
+ <TouchableOpacity
1074
+ style={styles.submitButton}
1075
+ onPress={() => console.log({ name, message })}
1076
+ >
1077
+ <Text style={styles.submitText}>Submit</Text>
1078
+ </TouchableOpacity>
1079
+ </View>
1080
+ );
1081
+ };
462
1082
 
463
- const start = useCallback(async () => {
464
- try {
465
- await Voice.start(locale);
466
- } catch (e) {
467
- setError('Failed to start');
468
- }
469
- }, [locale]);
1083
+ const styles = StyleSheet.create({
1084
+ container: {
1085
+ flex: 1,
1086
+ padding: 20,
1087
+ backgroundColor: '#fff',
1088
+ },
1089
+ title: {
1090
+ fontSize: 24,
1091
+ fontWeight: 'bold',
1092
+ marginBottom: 30,
1093
+ textAlign: 'center',
1094
+ },
1095
+ inputGroup: {
1096
+ marginBottom: 20,
1097
+ },
1098
+ label: {
1099
+ fontSize: 16,
1100
+ marginBottom: 8,
1101
+ fontWeight: '600',
1102
+ },
1103
+ inputRow: {
1104
+ flexDirection: 'row',
1105
+ alignItems: 'center',
1106
+ },
1107
+ input: {
1108
+ flex: 1,
1109
+ borderWidth: 1,
1110
+ borderColor: '#ddd',
1111
+ padding: 12,
1112
+ borderRadius: 8,
1113
+ marginRight: 10,
1114
+ },
1115
+ textArea: {
1116
+ height: 100,
1117
+ textAlignVertical: 'top',
1118
+ },
1119
+ micBtn: {
1120
+ width: 45,
1121
+ height: 45,
1122
+ borderRadius: 22.5,
1123
+ backgroundColor: '#007AFF',
1124
+ justifyContent: 'center',
1125
+ alignItems: 'center',
1126
+ },
1127
+ recording: {
1128
+ backgroundColor: '#FF3B30',
1129
+ },
1130
+ submitButton: {
1131
+ backgroundColor: '#34C759',
1132
+ padding: 15,
1133
+ borderRadius: 8,
1134
+ alignItems: 'center',
1135
+ marginTop: 20,
1136
+ },
1137
+ submitText: {
1138
+ color: '#fff',
1139
+ fontSize: 18,
1140
+ fontWeight: 'bold',
1141
+ },
1142
+ });
1143
+ ```
1144
+
1145
+ ### Example 5: Voice Commands (Advanced)
470
1146
 
471
- const stop = useCallback(async () => {
472
- try {
473
- await Voice.stop();
474
- } catch (e) {
475
- setError('Failed to stop');
1147
+ ```typescript
1148
+ import React, { useState, useEffect } from 'react';
1149
+ import { View, Text, StyleSheet, FlatList } from 'react-native';
1150
+ import { VoiceMicrophone } from 'react-native-voice-ts';
1151
+
1152
+ const VoiceCommands = () => {
1153
+ const [command, setCommand] = useState('');
1154
+ const [history, setHistory] = useState<string[]>([]);
1155
+
1156
+ useEffect(() => {
1157
+ if (command) {
1158
+ handleCommand(command);
476
1159
  }
477
- }, []);
1160
+ }, [command]);
1161
+
1162
+ const handleCommand = (text: string) => {
1163
+ const lowerText = text.toLowerCase();
1164
+
1165
+ if (lowerText.includes('hello') || lowerText.includes('hi')) {
1166
+ addToHistory('👋 Hello there!');
1167
+ } else if (lowerText.includes('time')) {
1168
+ addToHistory(`🕒 Current time: ${new Date().toLocaleTimeString()}`);
1169
+ } else if (lowerText.includes('date')) {
1170
+ addToHistory(`📅 Today's date: ${new Date().toLocaleDateString()}`);
1171
+ } else if (lowerText.includes('clear')) {
1172
+ setHistory([]);
1173
+ } else {
1174
+ addToHistory(`❓ Unknown command: "${text}"`);
1175
+ }
1176
+ };
1177
+
1178
+ const addToHistory = (message: string) => {
1179
+ setHistory((prev) => [message, ...prev]);
1180
+ };
478
1181
 
479
- return { isRecording, results, error, start, stop };
1182
+ return (
1183
+ <View style={styles.container}>
1184
+ <Text style={styles.title}>Voice Commands</Text>
1185
+ <Text style={styles.subtitle}>
1186
+ Try: "Hello", "What time is it?", "What's the date?", "Clear"
1187
+ </Text>
1188
+
1189
+ <VoiceMicrophone
1190
+ onSpeechResult={setCommand}
1191
+ autoStart={false}
1192
+ >
1193
+ {({ isRecording, recognizedText, start, stop }) => (
1194
+ <View style={styles.controlPanel}>
1195
+ <TouchableOpacity
1196
+ style={[styles.button, isRecording && styles.recording]}
1197
+ onPress={isRecording ? stop : start}
1198
+ >
1199
+ <Text style={styles.buttonText}>
1200
+ {isRecording ? '🔴 Listening...' : '🎤 Speak Command'}
1201
+ </Text>
1202
+ </TouchableOpacity>
1203
+ {recognizedText && (
1204
+ <Text style={styles.recognizedText}>"{recognizedText}"</Text>
1205
+ )}
1206
+ </View>
1207
+ )}
1208
+ </VoiceMicrophone>
1209
+
1210
+ <FlatList
1211
+ data={history}
1212
+ style={styles.list}
1213
+ keyExtractor={(_, index) => index.toString()}
1214
+ renderItem={({ item }) => (
1215
+ <View style={styles.historyItem}>
1216
+ <Text style={styles.historyText}>{item}</Text>
1217
+ </View>
1218
+ )}
1219
+ ListEmptyComponent={
1220
+ <Text style={styles.emptyText}>No commands yet...</Text>
1221
+ }
1222
+ />
1223
+ </View>
1224
+ );
480
1225
  };
1226
+
1227
+ const styles = StyleSheet.create({
1228
+ container: {
1229
+ flex: 1,
1230
+ padding: 20,
1231
+ backgroundColor: '#fff',
1232
+ },
1233
+ title: {
1234
+ fontSize: 24,
1235
+ fontWeight: 'bold',
1236
+ textAlign: 'center',
1237
+ marginBottom: 10,
1238
+ },
1239
+ subtitle: {
1240
+ fontSize: 12,
1241
+ color: '#666',
1242
+ textAlign: 'center',
1243
+ marginBottom: 20,
1244
+ },
1245
+ controlPanel: {
1246
+ marginBottom: 20,
1247
+ },
1248
+ button: {
1249
+ backgroundColor: '#007AFF',
1250
+ padding: 15,
1251
+ borderRadius: 10,
1252
+ alignItems: 'center',
1253
+ },
1254
+ recording: {
1255
+ backgroundColor: '#FF3B30',
1256
+ },
1257
+ buttonText: {
1258
+ color: '#fff',
1259
+ fontSize: 16,
1260
+ fontWeight: 'bold',
1261
+ },
1262
+ recognizedText: {
1263
+ textAlign: 'center',
1264
+ marginTop: 10,
1265
+ fontSize: 14,
1266
+ color: '#666',
1267
+ fontStyle: 'italic',
1268
+ },
1269
+ list: {
1270
+ flex: 1,
1271
+ },
1272
+ historyItem: {
1273
+ backgroundColor: '#f0f0f0',
1274
+ padding: 15,
1275
+ borderRadius: 8,
1276
+ marginBottom: 10,
1277
+ },
1278
+ historyText: {
1279
+ fontSize: 16,
1280
+ },
1281
+ emptyText: {
1282
+ textAlign: 'center',
1283
+ color: '#999',
1284
+ marginTop: 50,
1285
+ fontSize: 16,
1286
+ },
1287
+ });
481
1288
  ```
482
1289
 
1290
+ **More examples in the repo:**
1291
+
1292
+ - [VoiceSearchExample.tsx](./example/src/VoiceSearchExample.tsx) - Full search bar implementation
1293
+ - [VoiceHookExample.tsx](./example/src/VoiceHookExample.tsx) - Hook usage with advanced features
1294
+ - [COMPONENT_USAGE.md](./COMPONENT_USAGE.md) - Comprehensive component guide
1295
+
1296
+ ---
1297
+
1298
+ ## 📦 Component & Hook Usage
1299
+
1300
+ ### Quick Links
1301
+
1302
+ - **[Complete Component Usage Guide](./COMPONENT_USAGE.md)** - Comprehensive guide with real-world examples
1303
+ - **[VoiceSearchExample](./example/src/VoiceSearchExample.tsx)** - Working search bar example
1304
+ - **[VoiceHookExample](./example/src/VoiceHookExample.tsx)** - Working hook usage example
1305
+
1306
+ ### Component Benefits
1307
+
1308
+ ✅ **No boilerplate** - Works out of the box
1309
+ ✅ **Automatic cleanup** - Handles all event listeners
1310
+ ✅ **Permission handling** - Built-in permission checks
1311
+ ✅ **Type-safe** - Full TypeScript support
1312
+ ✅ **Customizable** - Use your own UI with render props
1313
+
483
1314
  ---
484
1315
 
485
1316
  ## ⚠️ Common Issues & Solutions