// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:rxdart/rxdart.dart'; import 'package:sound_stream/sound_stream.dart'; // TODO import Dialogflow class Chat extends StatefulWidget { Chat({Key key}) : super(key: key); @override _ChatState createState() => _ChatState(); } class _ChatState extends State { final List _messages = []; final TextEditingController _textController = TextEditingController(); bool _isRecording = false; RecorderStream _recorder = RecorderStream(); StreamSubscription _recorderStatus; StreamSubscription> _audioStreamSubscription; BehaviorSubject> _audioStream; // TODO DialogflowGrpcV2Beta1 class instance @override void initState() { super.initState(); initPlugin(); } @override void dispose() { _recorderStatus?.cancel(); _audioStreamSubscription?.cancel(); super.dispose(); } // Platform messages are asynchronous, so we initialize in an async method. Future initPlugin() async { _recorderStatus = _recorder.status.listen((status) { if (mounted) setState(() { _isRecording = status == SoundStreamStatus.Playing; }); }); await Future.wait([ _recorder.initialize() ]); // TODO Get a Service account } void stopStream() async { await _recorder.stop(); await _audioStreamSubscription?.cancel(); await _audioStream?.close(); } void handleSubmitted(text) async { print(text); _textController.clear(); //TODO Dialogflow Code } void handleStream() async { _recorder.start(); _audioStream = BehaviorSubject>(); _audioStreamSubscription = _recorder.audioStream.listen((data) { print(data); _audioStream.add(data); }); // TODO Create SpeechContexts // Create an audio InputConfig // TODO Make the streamingDetectIntent call, with the InputConfig and the audioStream // TODO Get the transcript and detectedIntent and show on screen } // The chat interface // //------------------------------------------------------------------------------------ @override Widget build(BuildContext context) { return Column(children: [ Flexible( child: ListView.builder( padding: EdgeInsets.all(8.0), reverse: true, itemBuilder: (_, int index) => _messages[index], itemCount: _messages.length, )), Divider(height: 1.0), Container( decoration: BoxDecoration(color: Theme.of(context).cardColor), child: IconTheme( data: IconThemeData(color: Theme.of(context).accentColor), child: Container( margin: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( children: [ Flexible( child: TextField( controller: _textController, onSubmitted: handleSubmitted, decoration: InputDecoration.collapsed(hintText: "Send a message"), ), ), Container( margin: EdgeInsets.symmetric(horizontal: 4.0), child: IconButton( icon: Icon(Icons.send), onPressed: () => handleSubmitted(_textController.text), ), ), IconButton( iconSize: 30.0, icon: Icon(_isRecording ? Icons.mic_off : Icons.mic), onPressed: _isRecording ? stopStream : handleStream, ), ], ), ), ) ), ]); } } //------------------------------------------------------------------------------------ // The chat message balloon // //------------------------------------------------------------------------------------ class ChatMessage extends StatelessWidget { ChatMessage({this.text, this.name, this.type}); final String text; final String name; final bool type; List otherMessage(context) { return [ new Container( margin: const EdgeInsets.only(right: 16.0), child: CircleAvatar(child: new Text('B')), ), new Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(this.name, style: TextStyle(fontWeight: FontWeight.bold)), Container( margin: const EdgeInsets.only(top: 5.0), child: Text(text), ), ], ), ), ]; } List myMessage(context) { return [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(this.name, style: Theme.of(context).textTheme.subtitle1), Container( margin: const EdgeInsets.only(top: 5.0), child: Text(text), ), ], ), ), Container( margin: const EdgeInsets.only(left: 16.0), child: CircleAvatar( child: Text( this.name[0], style: TextStyle(fontWeight: FontWeight.bold), )), ), ]; } @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(vertical: 10.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: this.type ? myMessage(context) : otherMessage(context), ), ); } }