Audience Polls during Live Stream - Flutter
Interactive polls are a great way to increase engagement during livestreams. Using VideoSDK’s PubSub mechanism, you can easily implement real-time audience polling, where viewers can vote and see live results instantly.
This guide walks you through how to create, send, and visualize poll results during a livestream.
Step 1: Creating and Publishing a Poll
To initiate a poll, use the PubSub object with a POLL topic. The poll structure should include a question and multiple options. This message will be published to all participants.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class PollHostView extends StatelessWidget {
  final Room room;
  const PollHostView({required this.room});
  void startPoll() {
    final pollData = {
      "id": DateTime.now().millisecondsSinceEpoch,
      "question": "Which feature do you love the most?",
      "options": ["Reactions", "Screen Share", "Whiteboard", "Polls"],
      "responses": {},
    };
    room.pubSub.publish(
      "POLL",
      jsonEncode(pollData),
      const PubSubPublishOptions(persist: true),
    );
  }
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: startPoll,
      child: const Text("Start Poll"),
    );
  }
}
Step 2: Subscribing to Polls and Displaying Options
Participants can listen to the POLL topic and render voting options dynamically based on the incoming data.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class PollAudienceView extends StatefulWidget {
  final Room room;
  const PollAudienceView({required this.room});
  @override
  State<PollAudienceView> createState() => _PollAudienceViewState();
}
class _PollAudienceViewState extends State<PollAudienceView> {
  Map<String, dynamic>? activePoll;
  @override
  void initState() {
    super.initState();
    widget.room.pubSub.subscribe("POLL", handlePollReceived);
  }
  @override
  void dispose() {
    widget.room.pubSub.unsubscribe("POLL", handlePollReceived);
    super.dispose();
  }
  void handlePollReceived(PubSubMessage message) {
    setState(() {
      activePoll = jsonDecode(message.message);
    });
  }
  void submitVote(String option) {
    if (activePoll != null) {
      widget.room.pubSub.publish(
        "POLL_RESPONSE",
        jsonEncode({
          "pollId": activePoll!['id'],
          "option": option,
        }),
        const PubSubPublishOptions(persist: true),
      );
    }
  }
  @override
  Widget build(BuildContext context) {
    if (activePoll == null) return const SizedBox();
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          activePoll!['question'],
          style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 10),
        ...(activePoll!['options'] as List<dynamic>).map((option) {
          return Padding(
            padding: const EdgeInsets.symmetric(vertical: 4),
            child: ElevatedButton(
              onPressed: () => submitVote(option.toString()),
              child: Text(option.toString()),
            ),
          );
        }).toList(),
      ],
    );
  }
}
Step 3: Aggregating and Displaying Poll Results
The host can subscribe to the POLL_RESPONSE topic to collect responses and render the result in real-time.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';
class PollResultsView extends StatefulWidget {
  final Room room;
  const PollResultsView({required this.room});
  @override
  State<PollResultsView> createState() => _PollResultsViewState();
}
class _PollResultsViewState extends State<PollResultsView> {
  final Map<String, Map<String, int>> results = {};
  @override
  void initState() {
    super.initState();
    widget.room.pubSub.subscribe("POLL_RESPONSE", handleResponse);
  }
  @override
  void dispose() {
    widget.room.pubSub.unsubscribe("POLL_RESPONSE", handleResponse);
    super.dispose();
  }
  void handleResponse(PubSubMessage message) {
    final data = jsonDecode(message.message);
    final pollId = data['pollId'].toString();
    final option = data['option'].toString();
    setState(() {
      final pollResult = results[pollId] ?? {};
      pollResult[option] = (pollResult[option] ?? 0) + 1;
      results[pollId] = pollResult;
    });
  }
  @override
  Widget build(BuildContext context) {
    if (results.isEmpty) return const Text("No poll results yet.");
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: results.entries.map((entry) {
        final pollId = entry.key;
        final pollResult = entry.value;
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("Poll ID: $pollId", style: const TextStyle(fontWeight: FontWeight.bold)),
            ...pollResult.entries.map((e) => Text("${e.key}: ${e.value} votes")).toList(),
            const SizedBox(height: 16),
          ],
        );
      }).toList(),
    );
  }
}
API Reference
The API references for all the methods and events utilized in this guide are provided below.
Got a Question? Ask us on discord

