Building a Chatbot with Murmuration and Flutter 🐦✨

Introduction

In this tutorial, we will create a simple chatbot application using the Murmuration framework and Flutter. This application will allow users to interact with a chatbot, sending messages and receiving responses in real-time. We will also implement local storage to save chat history using the shared_preferences package.

Prerequisites

Step 1: Setting Up Your Flutter Project

  1. Create a new Flutter project:
    flutter create murmuration_chatbot
    cd murmuration_chatbot
  2. Add dependencies: Open pubspec.yaml and add the following dependencies:
    dependencies:
      flutter:
        sdk: flutter
      murmuration: ^latest_version
      shared_preferences: ^latest_version
  3. Install the dependencies:
    flutter pub get

Step 2: Building the Chatbot Application

2.1 Main Application Entry Point

In lib/main.dart, we will set up the main entry point of our application:

import 'package:flutter/material.dart';
import 'package:murmuration/murmuration.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Murmuration Chatbot',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ChatScreen(),
    );
  }
}

2.2 Creating the Chat Screen

Next, we will create the ChatScreen widget, which will handle user interactions and display chat messages.

class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State {
  final TextEditingController _controller = TextEditingController();
  final List _messages = [];
  late Agent _agent;
  String _selectedLanguage = 'en';

  @override
  void initState() {
    super.initState();
    _initializeAgent();
    _loadMessages();
  }

  Future _initializeAgent() async {
    final config = MurmurationConfig(apiKey: 'add-your-api-key');
    _agent = await Murmuration(config).createAgent({
      'role': 'Assistant',
      'language': _selectedLanguage,
    });
  }

  Future _loadMessages() async {
    final prefs = await SharedPreferences.getInstance();
    final savedMessages = prefs.getStringList('chat_history') ?? [];
    setState(() {
      _messages.addAll(savedMessages.map((msg) => Message.fromJson(msg)).toList());
    });
  }

  Future _saveMessage(Message message) async {
    final prefs = await SharedPreferences.getInstance();
    final savedMessages = prefs.getStringList('chat_history') ?? [];
    savedMessages.add(message.toJson());
    await prefs.setStringList('chat_history', savedMessages);
  }

  Future _sendMessage() async {
    if (_controller.text.isEmpty) return;

    final userMessage = Message(role: 'user', content: _controller.text);
    setState(() {
      _messages.add(userMessage);
    });
    _controller.clear();

    await _saveMessage(userMessage);

    final result = await _agent.call(userMessage.content);
    final assistantMessage = Message(role: 'assistant', content: result.output);

    setState(() {
      _messages.add(assistantMessage);
    });
    await _saveMessage(assistantMessage);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Murmuration Chatbot'),
        actions: [
          DropdownButton(
            value: _selectedLanguage,
            items: ['en', 'es', 'fr', 'de']
                .map>((String value) {
              return DropdownMenuItem(
                value: value,
                child: Text(value),
              );
            }).toList(),
            onChanged: (String? newValue) {
              setState(() {
                _selectedLanguage = newValue!;
                _initializeAgent();
              });
            },
          ),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.all(8.0),
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                final message = _messages[index];
                return _buildMessageTile(message);
              },
            ),
          ),
          _buildInputField(),
        ],
      ),
    );
  }

  Widget _buildMessageTile(Message message) {
    final isUser Message = message.role == 'user';
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 4.0),
      padding: const EdgeInsets.all(10.0),
      decoration: BoxDecoration(
        color: isUser Message ? Colors.blue[100] : Colors.grey[200],
        borderRadius: BorderRadius.circular(8.0),
      ),
      alignment: isUser Message ? Alignment.centerRight : Alignment.centerLeft,
      child: Column(
        crossAxisAlignment: isUser Message ? CrossAxisAlignment.end : CrossAxisAlignment.start,
        children: [
          Text(
            message.role,
            style: TextStyle(
              fontWeight: FontWeight.bold,
              color: isUser Message ? Colors.blue : Colors.black,
            ),
          ),
          SizedBox(height: 4.0),
          Text(
            message.content,
            style: TextStyle(fontSize: 16.0),
          ),
        ],
      ),
    );
  }

  Widget _buildInputField() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _controller,
              decoration: InputDecoration(
                hintText: 'Type your message...',
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(30.0),
                  borderSide: BorderSide(color: Colors.blue),
                ),
                filled: true,
                fillColor: Colors.white,
              ),
            ),
          ),
          const SizedBox(width: 8.0),
          IconButton(
            icon: const Icon(Icons.send, color: Colors.blue),
            onPressed: _sendMessage,
          ),
        ],
      ),
    );
  }
}

// Message class to represent a chat message
class Message {
  final String role;
  final String content;

  Message({required this.role, required this.content});

  String toJson() {
    return '{"role": "$role", "content": "$content"}';
  }

  static Message fromJson(String json) {
    final data = jsonDecode(json);
    return Message(role: data['role'], content: data['content']);
  }
}

Conclusion

Congratulations! You have successfully built a simple chatbot application using the Murmuration framework and Flutter. This application allows users to send messages and receive responses from a chatbot, with chat history saved locally.

Feel free to expand upon this example by adding more features, such as user authentication, advanced message handling, or integrating additional APIs.