augnito_flutter_sdk 0.0.4 augnito_flutter_sdk: ^0.0.4 copied to clipboard
Use the Augnito Flutter SDK to enable Text To Speech and voice commands into a Flutter application.
import 'package:augnito_flutter_sdk/config/augnito_api_server.dart';
import 'package:augnito_flutter_sdk/config/augnito_config.dart';
import 'package:augnito_flutter_sdk/dictation/dictation_manager.dart';
import 'package:augnito_flutter_sdk/dictation/models/action_recipe.dart';
import 'package:augnito_flutter_sdk/dictation/support/dictation_error.dart';
import 'package:augnito_flutter_sdk/text/utils/text_field_processor.dart';
import 'package:augnito_flutter_sdk_example/app_helpers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'dart:async';
import 'package:collection/collection.dart';
import 'command_list.dart';
import 'models/commands.dart';
Future main() async {
await dotenv.load(fileName: ".env");
runApp(MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
colorScheme: ColorScheme.fromSwatch(
primarySwatch:
AppHelpers.createMaterialColor(const Color.fromRGBO(0, 77, 99, 1)),
),
),
home: const HomeScreen(),
));
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
/// Dictionary of available editors.
abstract class AvailableEditors {
static const String patientId = "patientId";
static const String diagnosis = "diagnosis";
static const String medication = "medication";
static const String history = "history";
}
/// Utility class to simplify the interactions with the fields on screen.
class SelectableEditor {
final String name;
final focusNode = FocusNode();
final textEditingController = TextEditingController();
SelectableEditor(this.name);
}
class _HomeScreenState extends State<HomeScreen> {
var _connected = false;
var _busy = false;
final _menuActionTextStart = "Tap here to start";
final _menuActionTextStop = "Listening...";
var _currentMenuActionText = "";
late DictationManager _dictationManager;
final AugnitoConfig _config = AugnitoConfig(
AugnitoAPIServer.india,
dotenv.env['ACCOUNT_CODE'] ?? "",
dotenv.env['ACCESS_KEY'] ?? "",
dotenv.env['LMID'] ?? "",
dotenv.env['USER_TAG'] ?? "",
sourceApp: dotenv.env['SOURCE_APP'] ?? "fluttersdkexample");
final selectableEditors = List<SelectableEditor>.from([
SelectableEditor(AvailableEditors.patientId),
SelectableEditor(AvailableEditors.diagnosis),
SelectableEditor(AvailableEditors.medication),
SelectableEditor(AvailableEditors.history)
]);
SelectableEditor? focusedEditor;
@override
void initState() {
super.initState();
_currentMenuActionText = _menuActionTextStart;
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
_dictationManager = DictationManager(_config,
onConnected: _onConnected,
onDisconnected: _onDisconnected,
onError: _onError,
onPartialResult: _onPartialResult,
onFinalResult: _onFinalResult,
onCommandResult: _onCommand,
enableLogs: true);
}
SelectableEditor _getEditorByName(String name) {
return selectableEditors.firstWhere((element) => element.name == name);
}
void _onPartialResult(String hypothesis) {
setState(() {
_currentMenuActionText = hypothesis.length > 15
? hypothesis.replaceRange(12, hypothesis.length, '...')
: hypothesis;
});
print("partial result: $hypothesis");
}
void _onFinalResult(String transcription) {
if (focusedEditor != null) {
TextFieldProcessor.addContent(
transcription, focusedEditor!.textEditingController);
}
setState(() {
_currentMenuActionText = _menuActionTextStop;
});
print("final result: $transcription");
}
void _onCommand(ActionRecipe actionRecipe) {
setState(() {
_currentMenuActionText = _menuActionTextStop;
});
if (focusedEditor == null) {
return;
}
switch (actionRecipe.name) {
case Commands.StopMic:
_onToggleDictation();
break;
case Commands.DeleteIt:
case Commands.DeleteThat:
case Commands.DeletePreviousWord:
case Commands.DeletePreviousLine:
_processStaticDelete(actionRecipe);
break;
case Commands.SelectWord:
_processSelectWord(actionRecipe);
break;
case Commands.SelectLine:
_processSelectLine(actionRecipe);
break;
case Commands.Goto:
_processGoToTextField(actionRecipe);
break;
default:
}
print("command: ${actionRecipe.name}");
print(actionRecipe.toString());
}
void _processGoToTextField(ActionRecipe actionRecipe) {
final control = actionRecipe.searchText.replaceAll(RegExp(r"\s+"), "");
final target = selectableEditors
.firstWhereOrNull((element) => element.name.toLowerCase() == control);
if (target != null) {
target.focusNode.requestFocus();
}
}
void _processSelectLine(ActionRecipe actionRecipe) {
TextFieldProcessor.selectLastLines(focusedEditor!.textEditingController,
offset: actionRecipe.chooseNumber);
if (actionRecipe.selectFor == Commands.Delete) {
TextFieldProcessor.deleteSelection(focusedEditor!.textEditingController);
}
}
void _processSelectWord(ActionRecipe actionRecipe) {
TextFieldProcessor.selectLastWords(focusedEditor!.textEditingController,
offset: actionRecipe.chooseNumber);
if (actionRecipe.selectFor == Commands.Delete) {
TextFieldProcessor.deleteSelection(focusedEditor!.textEditingController);
}
}
void _processStaticDelete(ActionRecipe actionRecipe) {
TextFieldProcessor.selectLastWords(focusedEditor!.textEditingController);
TextFieldProcessor.deleteSelection(focusedEditor!.textEditingController);
}
void _onConnected() {
setState(() {
_connected = true;
_busy = false;
_currentMenuActionText = _menuActionTextStop;
});
}
void _onDisconnected() {
setState(() {
_connected = false;
_busy = false;
_currentMenuActionText = _menuActionTextStart;
});
}
void _onError(DictationError error) {
print("error ${error.errorMessage}");
}
void _onChangeFocus(SelectableEditor editor) {
setState(() {
focusedEditor = editor;
});
}
void _onToggleDictation() {
if (_busy) {
return;
}
setState(() {
_busy = true;
});
_dictationManager.toggleDictation();
}
@override
void dispose() {
for (var editor in selectableEditors) {
editor.textEditingController.dispose();
editor.focusNode.dispose();
}
_dictationManager.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: PopupMenuButton(
// add icon, by default "3 dot" icon
icon: const Icon(Icons.more_vert),
itemBuilder: (context) {
return [
const PopupMenuItem<int>(
value: 0,
child: Text("COMMAND HELP"),
),
];
},
onSelected: (value) {
if (value == 0) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CommandListPage()),
);
}
}),
actions: [
Opacity(
opacity: _busy ? 0.3 : 1,
child: TextButton(
onPressed: _onToggleDictation,
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
),
child: Row(children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Text(_currentMenuActionText),
),
_connected
? Image.asset("assets/stop_mic.png")
: Image.asset("assets/start_mic.png")
]),
),
)
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: <Widget>[
Focus(
onFocusChange: (value) => {
if (value)
{
_onChangeFocus(
_getEditorByName(AvailableEditors.patientId))
}
},
child: TextField(
focusNode:
_getEditorByName(AvailableEditors.patientId).focusNode,
autofocus: true,
keyboardType: TextInputType.none,
controller: _getEditorByName(AvailableEditors.patientId)
.textEditingController,
decoration: const InputDecoration(
alignLabelWithHint: true,
label: Text('Patient Id'),
border: OutlineInputBorder(),
),
),
),
const SizedBox(height: 16),
Focus(
onFocusChange: (value) => {
if (value)
{
_onChangeFocus(
_getEditorByName(AvailableEditors.diagnosis))
}
},
child: TextField(
focusNode:
_getEditorByName(AvailableEditors.diagnosis).focusNode,
keyboardType: TextInputType.none,
controller: _getEditorByName(AvailableEditors.diagnosis)
.textEditingController,
decoration: const InputDecoration(
alignLabelWithHint: true,
label: Text('Diagnosis'),
border: OutlineInputBorder(),
),
),
),
const SizedBox(height: 16),
Focus(
onFocusChange: (value) => {
if (value)
{
_onChangeFocus(
_getEditorByName(AvailableEditors.medication))
}
},
child: TextField(
focusNode:
_getEditorByName(AvailableEditors.medication).focusNode,
keyboardType: TextInputType.none,
controller: _getEditorByName(AvailableEditors.medication)
.textEditingController,
decoration: const InputDecoration(
alignLabelWithHint: true,
label: Text('Medication'),
border: OutlineInputBorder(),
),
),
),
const SizedBox(height: 16),
Focus(
onFocusChange: (value) => {
if (value)
{_onChangeFocus(_getEditorByName(AvailableEditors.history))}
},
child: TextField(
focusNode:
_getEditorByName(AvailableEditors.history).focusNode,
keyboardType: TextInputType.none,
maxLines: 5,
controller: _getEditorByName(AvailableEditors.history)
.textEditingController,
decoration: const InputDecoration(
label: Text('History'),
alignLabelWithHint: true,
border: OutlineInputBorder(),
),
),
),
],
),
),
),
);
}
}