Реализация Flutter FactsBot с использованием DialogFlow

Первоначально опубликовано 20 августа 2019 г.

Фон

В этой статье мы научимся интегрировать ДиалогФлоу в приложении Flutter для обогащения разговорного опыта. Я буду использовать пример приложения, который мы создали ранее в своей предыдущей статье. Разработка кроссплатформенного прототипа Flutter для Landing Page (Web-Hummingbird, Android, iOS). Мы будем интегрировать диалоговый API DialogFlow в собственное приложение Flutter.

Вы можете обратиться к предыдущим статьям здесь:

  1. Разработка кроссплатформенного прототипа Flutter для целевой страницы
  2. Делаем кроссплатформенную целевую страницу Flutter адаптивной
  3. Использование тем Flutter для кроссплатформенной целевой страницы (Web-Hummingbird, Android, iOS)

Посмотрите сопутствующее видео:

Введение

В этой статье я покажу вам, как мы можем интегрировать ДиалогФлоу в приложении Flutter для обогащения разговорного опыта. Мы сохраним его в забавном приложении для демонстрационных целей. Мы добавим значок чата на нашу целевую страницу. Нажав на этот значок чата, пользователь попадет на экран, похожий на чат. Всякий раз, когда пользователь вводит предложение или слово, состоящее из «Flutter», наш «Flutter Facts Bot» представляет пользователю факт о Flutter. 😃 Простой !

Чтобы адаптировать наше существующее приложение целевой страницы к этому новому разговорному стилю, мы предпримем следующие шаги:

  1. Настройка ДиалогФлоу
  2. Интеграция приложения с DialogFlow
  3. Добавление чата в виде значка на целевой странице
  4. Разработка интерфейса FlutterFactsDialogFlow.

DialogFlow-Флаттер

Настройка ДиалогФлоу

  • Создайте учетную запись на ДиалогФлоу. Это бесплатно создать учетную запись.

Диалоговый поток #1

  • Создайте агента, нажав кнопку «Создать агента», как показано на изображении ниже:

Диалоговый поток #2

  • Назначьте облачный проект Google. Перейдите к Облачная консоль Google для создания нового проекта, если у вас его еще нет. Я выберу свой существующий облачный проект Google под названием «flutter-to-fly».

Диалоговый поток #3

  • Создать/выбрать намерение. В этом уроке я буду использовать намерение приветствия по умолчанию. Это самое первое намерение, представленное пользователю в начале разговора.

Диалоговый поток #4

  • Тренировать намерение. На этом шаге добавьте обучающие фразы. Учебные фразы будут включать слова или предложения, которые, по вашему мнению, пользователь может использовать для начала разговора. Я использую слово «флаттер» в различных сочетаниях. Если у пользователя в предложении есть ключевое слово «флаттер», ему будет представлен факт о флаттере. Нажмите «Сохранить», когда закончите добавлять все возможные ключевые слова/фразы. Начнется обучение агентов.

Обучение DialogFlow #1

  • Добавить ответы. Я буду добавлять простые текстовые ответы для этого урока. Текст ответа — это вывод фраз, опробованных пользователем. Если найдено совпадение для введенного пользователем текста, пользователю будет возвращен один из текстовых ответов. В нашем случае, если пользователь говорит «трепетать» в своем запросе, будет возвращен факт Flutter.

Обучение ДиалогФлоу #2

  • Вы можете попробовать ответы на правой панели, набрав или произнеся фразы, чтобы проверить выполнение.

Обучение ДиалогФлоу #2

Агент DialogFlow обучен и готов возвращать текстовые ответы по умолчанию!

Интеграция приложения с DialogFlow

Теперь мы хотим интегрировать наше недавно обученное намерение DialogFlow в функцию Flutter Facts нашего приложения.

  • Направляйтесь к Облачная консоль Google
    • Выберите проект Google Cloud.
    • Выберите «API и сервисы».
    • Нажмите на учетные данные.

Учетные данные GC №1

  • Щелкните раскрывающееся меню «Создать учетные данные» и выберите параметр «Ключ служебной учетной записи».

Учетные данные GC # 2

  • Выберите опцию «Интеграция DialogFlow» в раскрывающемся списке «Учетная запись службы». Выберите рекомендуемый формат «JSON» для типа ключа.

Учетные данные GC #3

  • Загрузите закрытый ключ на свой компьютер. Храните это в безопасном месте для себя. НЕ вносите этот ключ в систему контроля версий.

Учетные данные GC #4

Примечание: Сохраните файл учетных данных в безопасном месте. НЕ регистрируйте этот файл в системе контроля версий, такой как Github, BitBucket и т. д.

  • Скопировать файл учетных данных flutter-to-fly-creds.json в assets каталог. я предпочитаю делать символическая ссылка для этого файла внутри assets папку, чтобы уберечь меня от случайной проверки на Github.

Учетные данные GC № 5

  • pubspec.yaml зависимости для плагина DialogFlow:
dependencies: flutter\_dialogflow: ^0.1.2

На этом этапе ваше приложение Flutter готово отправлять запросы к API DialogFlow.

Добавление чата в виде значка на целевой странице

Давайте добавим значок чата, чтобы вызвать диалог с 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

FlutterFactsDialogFlow

FlutterFactsBot в действии! *

Вот как должен выглядеть журнал взаимодействия пользователя и FlutterFactsBot:

FlutterFactsDialogFlow

Давайте погрузимся в кодирование интерфейса прямо сейчас!

Покажи мне код

Когда пользователь нажимает на значок чата, он запускается 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 выделяется зеленым цветом.

FlutterFactsDialogFlow

Давайте оформим заказ 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 😃

Понравилась статья? Не нашли интересующую вас тему? Пожалуйста, оставьте комментарии или напишите мне о темах, которые вы хотели бы, чтобы я написал Кстати, я люблю и кексы, и кофе :)

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *