Реализация Flutter FactsBot с использованием DialogFlow
Первоначально опубликовано 20 августа 2019 г.
Фон
В этой статье мы научимся интегрировать ДиалогФлоу в приложении Flutter для обогащения разговорного опыта. Я буду использовать пример приложения, который мы создали ранее в своей предыдущей статье. Разработка кроссплатформенного прототипа Flutter для Landing Page (Web-Hummingbird, Android, iOS). Мы будем интегрировать диалоговый API DialogFlow в собственное приложение Flutter.
Вы можете обратиться к предыдущим статьям здесь:
- Разработка кроссплатформенного прототипа Flutter для целевой страницы
- Делаем кроссплатформенную целевую страницу Flutter адаптивной
- Использование тем Flutter для кроссплатформенной целевой страницы (Web-Hummingbird, Android, iOS)
Посмотрите сопутствующее видео:
Введение
В этой статье я покажу вам, как мы можем интегрировать ДиалогФлоу в приложении Flutter для обогащения разговорного опыта. Мы сохраним его в забавном приложении для демонстрационных целей. Мы добавим значок чата на нашу целевую страницу. Нажав на этот значок чата, пользователь попадет на экран, похожий на чат. Всякий раз, когда пользователь вводит предложение или слово, состоящее из «Flutter», наш «Flutter Facts Bot» представляет пользователю факт о Flutter. Простой !
Чтобы адаптировать наше существующее приложение целевой страницы к этому новому разговорному стилю, мы предпримем следующие шаги:
- Настройка ДиалогФлоу
- Интеграция приложения с DialogFlow
- Добавление чата в виде значка на целевой странице
- Разработка интерфейса FlutterFactsDialogFlow.
Настройка ДиалогФлоу
- Создайте учетную запись на ДиалогФлоу. Это бесплатно создать учетную запись.
- Создайте агента, нажав кнопку «Создать агента», как показано на изображении ниже:
- Назначьте облачный проект Google. Перейдите к Облачная консоль Google для создания нового проекта, если у вас его еще нет. Я выберу свой существующий облачный проект Google под названием «flutter-to-fly».
- Создать/выбрать намерение. В этом уроке я буду использовать намерение приветствия по умолчанию. Это самое первое намерение, представленное пользователю в начале разговора.
- Тренировать намерение. На этом шаге добавьте обучающие фразы. Учебные фразы будут включать слова или предложения, которые, по вашему мнению, пользователь может использовать для начала разговора. Я использую слово «флаттер» в различных сочетаниях. Если у пользователя в предложении есть ключевое слово «флаттер», ему будет представлен факт о флаттере. Нажмите «Сохранить», когда закончите добавлять все возможные ключевые слова/фразы. Начнется обучение агентов.
- Добавить ответы. Я буду добавлять простые текстовые ответы для этого урока. Текст ответа — это вывод фраз, опробованных пользователем. Если найдено совпадение для введенного пользователем текста, пользователю будет возвращен один из текстовых ответов. В нашем случае, если пользователь говорит «трепетать» в своем запросе, будет возвращен факт Flutter.
- Вы можете попробовать ответы на правой панели, набрав или произнеся фразы, чтобы проверить выполнение.
Агент DialogFlow обучен и готов возвращать текстовые ответы по умолчанию!
Интеграция приложения с DialogFlow
Теперь мы хотим интегрировать наше недавно обученное намерение DialogFlow в функцию Flutter Facts нашего приложения.
- Направляйтесь к Облачная консоль Google
- Выберите проект Google Cloud.
- Выберите «API и сервисы».
- Нажмите на учетные данные.
- Щелкните раскрывающееся меню «Создать учетные данные» и выберите параметр «Ключ служебной учетной записи».
- Выберите опцию «Интеграция DialogFlow» в раскрывающемся списке «Учетная запись службы». Выберите рекомендуемый формат «JSON» для типа ключа.
- Загрузите закрытый ключ на свой компьютер. Храните это в безопасном месте для себя. НЕ вносите этот ключ в систему контроля версий.
Примечание: Сохраните файл учетных данных в безопасном месте. НЕ регистрируйте этот файл в системе контроля версий, такой как Github, BitBucket и т. д.
- Скопировать файл учетных данных
flutter-to-fly-creds.json
вassets
каталог. я предпочитаю делать символическая ссылка для этого файла внутриassets
папку, чтобы уберечь меня от случайной проверки на Github.
pubspec.yaml
зависимости для плагина DialogFlow:
dependencies: flutter\_dialogflow: ^0.1.2
На этом этапе ваше приложение Flutter готово отправлять запросы к API DialogFlow.
Добавление чата в виде значка на целевой странице
Давайте добавим значок чата, чтобы вызвать диалог с DialogFlow. При нажатии на этот значок чата откроется новая страница с полем ввода, где пользователь может ввести свой запрос. Для каждого запроса, введенного пользователем, будет получен факт флаттера из DialogFlow.
В этом разделе я добавлю значок чата в правом нижнем углу целевой страницы, как показано ниже:
Я создам файл, скажем widgets_lib.dart
чтобы отслеживать все мои многоразовые виджеты. Виджет factBot
это FloatingActionButton
для запуска пользовательского интерфейса FlutterFacts. Вы бы заметили, что onPressed:
Свойство отправляет сообщение маршрутизатору, чтобы открыть пользовательский интерфейс FlutterFacts. FACTS_DIALOGFLOW
строка похожа на сопоставление ключ-значение между идентификатором страницы и целевой страницей, которая должна запускаться в результате.
Widget factBot(BuildContext context) {
return Container(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
materialTapTargetSize: MaterialTapTargetSize.padded,
child: Center(
child: Icon(Icons.chat),
),
elevation: 4.0,
backgroundColor: MyColors.blue1,
onPressed: () => Navigator.pushNamed(context, FACTS_DIALOGFLOW),
)
);
}
Навигация в приложении:
Я буду использовать маршрутизацию страниц для перехода с одной страницы на другую в этом приложении Flutter. Давайте быстро рассмотрим router.dart
. Этот файл определяет строку для представления каждой страницы и использует RouteSettings
атрибут, чтобы открыть подходящую целевую страницу. Когда routeSettings.name
Спички FACTS_DIALOGFLOW
ценность, FlutterFactsDialogFlow()
страница откроется.
const String FACTS_DIALOGFLOW = "FACTS_DIALOGFLOW";
Route<dynamic> generateRoute(RouteSettings routeSettings) {
switch(routeSettings.name) {
case '/':
return MaterialPageRoute(builder: (context) => MyHomePage());
break;
case FACTS_DIALOGFLOW:
return MaterialPageRoute(builder: (context) => FlutterFactsDialogFlow());
}
}
Давайте посмотрим, какие изменения нам нужно внести в main.dart
для поддержки этой стратегии навигации на основе маршрутизации:
import 'router.dart' as router;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
...
onGenerateRoute: router.generateRoute,
initialRoute: '/',
);
}
}
Вы заметите, что я импортировал router.dart
в первую очередь, а затем установить onGenerateRoute
свойство MaterialApp
для перенаправления на данную страницу. Не забудьте предоставить initialRoute
атрибут. Этот атрибут определяет, какая страница будет открыта в первую очередь после запуска приложения.
Взаимодействие пользователя и бота FlutterFacts реализовано в FlutterFactsDialogFlow()
учебный класс. Давайте проверим детали в следующем разделе.
Разработка интерфейса FlutterFactsDialogFlow.
В этом разделе я создам простой интерфейс в виде окна чата для взаимодействия с FlutterFactsBot. Интерфейс будет иметь следующие компоненты в дополнение к AppBar
:
- Поле ввода пользователя внизу
- Верхняя часть для отображения журнала взаимодействия между пользователем и FlutterFactsBot.
- Значки пользователя и FlutterFactsBot
FlutterFactsBot в действии! *
Вот как должен выглядеть журнал взаимодействия пользователя и FlutterFactsBot:
Давайте погрузимся в кодирование интерфейса прямо сейчас!
Покажи мне код
Когда пользователь нажимает на значок чата, он запускается FlutterFactsDialogFlow
страница, которая StatefulWidget
. Этот виджет будет иметь список фактических сообщений _messages
для отображения журнала взаимодействия между пользователем и FlutterFactsBot. Этот список будет отображаться в обратном порядке, то есть самые последние сообщения будут внизу.
Страница состоит из двух частей: appBar
& body
. AppBar имеет заголовок для страницы, тогда как часть тела имеет три виджета внутри Column
виджет. Flexible
виджет содержит ListView
показывать _messages
список в обратном порядке. Divider
виджет используется для визуального разделения журнала взаимодействия и текстового поля _queryInputWidget
для ввода запроса пользователя.
class FlutterFactsDialogFlow extends StatefulWidget {
FlutterFactsDialogFlow({Key key, this.title}) : super(key: key);
final String title;
@override
_FlutterFactsDialogFlowState createState() => new _FlutterFactsDialogFlowState();
}
class _FlutterFactsDialogFlowState extends State<FlutterFactsDialogFlow> {
final List<FactsMessage> _messages = <FactsMessage>[];
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Flutter Facts"),
),
body: Column(children: <Widget>[
Flexible(
child: ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: true, //To keep the latest messages at the bottom
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
)),
Divider(height: 1.0),
Container(
decoration: new BoxDecoration(color: Theme.of(context).cardColor),
child: _queryInputWidget(context),
),
]),
);
}
}
Давайте исследуем _queryInputWidget
виджет для ввода текста запроса пользователя. Это область, где пользователь может ввести текст своего запроса. Он состоит из двух частей: TextField
виджет для ввода текста запроса и IconButton
для отправки запроса. TextField
виджету нужен controller
а также onSubmitted
атрибуты. onSubmitted
атрибут вызывает данный метод _submitQuery
в нашем случае, как только пользователь закончит ввод текста и нажмет Enter. IconButton
виджет — это еще один способ отправить текст в DialogFlow. Это onPressed
звонки _submitQuery
также.
class _FlutterFactsDialogFlowState extends State<FlutterFactsDialogFlow> {
final TextEditingController _textController = new TextEditingController();
Widget _queryInputWidget(BuildContext context) {
return Container(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: Row(
children: <Widget>[
Flexible(
child: TextField(
controller: _textController,
onSubmitted: _submitQuery,
decoration: InputDecoration.collapsed(hintText: "Send a message"),
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 4.0),
child: IconButton(
icon: Icon(Icons.send),
onPressed: () => _submitQuery(_textController.text)),
),
],
),
),
);
}
...
}
Давайте посмотрим, что _submitQuery
делает, когда пользователь заканчивает вводить свой запрос о Flutter. Как вы видите в коде ниже, когда _submitQuery
вызывается, первое, что он делает, это очищает ввод TextField
чтобы подготовить его к следующему взаимодействию с пользователем. Это занимает TextField
текст и создает структуру данных FactsMessage
для отображения журнала взаимодействия. Вы можете заметить несколько вещей здесь. _submitQuery
принимает ввод как text
. Когда _submitQuery
вызывается из TextField
виджет, text
передается этому методу неявно. Тогда как, когда _submitQuery
вызывается из IconButton
х onPressed
текст метода извлекается из _textController
и перешел в _submitQuery
метод.
void _submitQuery(String text) {
_textController.clear();
FactsMessage message = new FactsMessage(
text: text,
name: "Priyanka",
type: true,
);
setState(() {
_messages.insert(0, message);
});
_dialogFlowResponse(text);
}
FactsMessage
х name
собственность Priyanka
для представления пользователя (в данном случае я жестко закодировал свое имя как пользователя для демонстрационных целей), который взаимодействует с ботом. ты увидишь это name
свойство изменится на Flutter Bot
когда FlutterMessage
строится в _dialogFlowResponse
. FactsMessage
структура данных использует name
свойство, чтобы показать, кому принадлежит сообщение. FactsMessage
это StatelessWidget
для отображения сообщения о взаимодействии между пользователем и так называемым FlutterFactsBot.
Он принимает три параметра:
text
: текст запроса/ответаname
: кому принадлежит текстtype
: логическое значение.true
для пользователя.false
значит бот.
type
параметр определяет, из какого формата сообщения следует отображать userMessage
или же botMessage
.
class FactsMessage extends StatelessWidget {
FactsMessage({this.text, this.name, this.type});
final String text;
final String name;
final bool type;
...
@override
Widget build(BuildContext context) {
return new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: this.type ? userMessage(context) : botMessage(context),
),
);
}
}
FactsMessage
виджет имеет два типа сообщений: userMessage
а также botMessage
. Они различаются выравниванием виджетов в строке сообщения.
На скриншоте ниже userMessage
выделено красным цветом и botMessage
выделяется зеленым цветом.
Давайте оформим заказ userMessage
код виджета ниже. Это List
виджетов, выровненных по краю экрана. Все сообщения пользователя будут отображаться в левой части экрана, заканчиваясь CircleAvatar
.
List<Widget> userMessage(context) {
return <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(this.name, style: Theme.of(context).textTheme.subhead),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
Container(
margin: const EdgeInsets.only(left: 16.0),
child: CircleAvatar(child: new Text(this.name[0])),
),
];
}
Сообщения FlutterFactsBot будут отображаться в правой части экрана, начиная с CircleAvatar
List<Widget> botMessage(context) {
return <Widget>[
Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(child: Text('Bot')),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(this.name,
style: TextStyle(fontWeight: FontWeight.bold)),
Container(
margin: const EdgeInsets.only(top: 5.0),
child: Text(text),
),
],
),
),
];
}
Давайте перейдем к последней части головоломки: получению ответа от API DialogFlow и его рендерингу.
Ответ диалогового окна: После отправки запроса и отображения сообщения пользователя в журнале взаимодействия запрос отправляется в DialogFlow, а ответ отображается прямо под компаньоном. userMessage
. Вот как запрос отправляется в API DialogFlow с использованием flutter_dialogflow
плагин:
void _dialogFlowResponse(query) async {
_textController.clear();
AuthGoogle authGoogle =
await AuthGoogle(fileJson: "assets/flutter-to-fly-creds.json").build();
Dialogflow dialogFlow =
Dialogflow(authGoogle: authGoogle, language: Language.english);
AIResponse response = await dialogFlow.detectIntent(query);
FactsMessage message = FactsMessage(
text: response.getMessage() ??
CardDialogflow(response.getListMessage()[0]).title,
name: "Flutter Bot",
type: false,
);
setState(() {
_messages.insert(0, message);
});
}
FactsMessage
создается и добавляется к _messages
журнал разговоров. Каждый раз, когда пользователь вводит текст запроса, состоящий из слова «трепетать», из DialogFlow будут возвращаться предварительно подготовленные текстовые ответы. В этом примере я использовал очень простые текстовые ответы по умолчанию в DialogFlow для выполнения. Однако вы можете показывать более динамичный контент, используя Выполнение вебхуков.
Вы закончили
Продолжай трепетать!
Исходный код доступна здесь
Ссылки/Кредиты:
Удачной готовки с Flutter
Понравилась статья? Не нашли интересующую вас тему? Пожалуйста, оставьте комментарии или напишите мне о темах, которые вы хотели бы, чтобы я написал