Video Call in Flutter - Video Streaming in Flutter

Published March 30, 2021

Currently we are lived in digital world, in this digital world we have to follow the digital trend. To connect the firiends, parents around the world would be done by making the video call to each other on the fly. Now a days many more mobile applications are supporting video calling feature. Flutter also provides a video calling feature. In this post we are going to implement audio, video call option in flutter with Agora RTC (Real Time Communication) SDK. Agora is a leading video, voice and live interactive streaming platform, helping developers deliver rich in-app experiences.

 

Let's get started.

In this Video call application we are going to implement below features with Agora mobile sdk.

1. join/leave a channel
2. Mute/unmute
3. Switching camera
4. Multiple video views Layout

 

Step 1: Create Flutter application

Step 2: Add required dependencies in pubspec.yaml file

dependencies:
  flutter:
    sdk: flutter
  agora_rtc_engine:
  permission_handler:
  provider:

 

Step 3: Now we need to create an application in Agora dashboard to get ApplicationID and Token value to handle video call with Agora.

Open Agora account at Agora Website

Make registration and login into account

Now navigate to menu and click on create button

Flutter Video call agora

 

Now it will show popup window, there enter project name and authentication mechanism type (It’s recommended to select Secure mode: APP ID + Token) and finally click on submit

 

Flutter Agora Video call

 

It will generate app id copy that APP ID.

Now we need to create a Token id for each Channel to make video call.

  • Agora uses a token to authenticate the users to join a channel for communication security.
  • For this, we need to click on the key icon on the Project management page

 

Agora flutter video call

 

While joining into a channel we need to pass same channel name which we created to generate temparary token.

 

Step 4: Update dart file with below code

hompeage.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'home_model.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  /// create a channelController to retrieve text value
  final _channelController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Consumer(builder: (context, HomeNotifier homeNotifier, child) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Video Call App'),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(20.0),
                child: Image.asset("assets/logo.JPG"),
              ),
              Center(
                child: Padding(
                  padding: const EdgeInsets.only(left: 16, right: 16),
                  child: Container(

                    child: Column(
                      children: <Widget>[
                        TextField(
                          controller: _channelController,
                          decoration: InputDecoration(
                            errorText: homeNotifier.validateError
                                ? 'Channel name is mandatory'
                                : null,
                            border: UnderlineInputBorder(
                              borderSide: BorderSide(width: 1),
                            ),
                            hintText: 'Channel name',
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(vertical: 20),
                          child: Row(
                            children: [
                              Expanded(
                                child: MaterialButton(
                                  onPressed: () => {
                                    homeNotifier.onJoin(
                                        context, _channelController.text),
                                  },
                                  child: Text('Join to Call'),
                                  color: Colors.purple,
                                  textColor: Colors.white,
                                ),
                              ),
                            ],
                          ),
                        )
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      );
    });
  }

  @override
  void dispose() {
    _channelController.dispose();
    super.dispose();
  }
}

 

main.dart

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: HomeNotifier()),
        ChangeNotifierProvider.value(value: CallNotifier()),
      ],
      child: MaterialApp(
        title: 'Video call app',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.purple,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

 

call_screen.dart

import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:flutter/material.dart';
import 'package:flutter_video_call_app/screens/base_widget.dart';
import 'package:flutter_video_call_app/screens/call/call_model.dart';

class CallScreen extends StatefulWidget {
  final String channelName;

  const CallScreen({Key key, this.channelName}) : super(key: key);

  @override
  _CallScreenState createState() => _CallScreenState();
}

class _CallScreenState extends State<CallScreen> {
  /// Helper function to get list of native views
  List<Widget> _getRenderViews(CallNotifier model) {
    final List<StatefulWidget> list = [];
    list.add(RtcLocalView.SurfaceView());
    model.users
        .forEach((int uid) => list.add(RtcRemoteView.SurfaceView(uid: uid)));
    return list;
  }

  /// Video view wrapper
  Widget _videoView(view) {
    return Expanded(child: Container(child: view));
  }

  /// Video view row wrapper
  Widget _expandedVideoRow(List<Widget> views) {
    final wrappedViews = views.map<Widget>(_videoView).toList();
    return Expanded(
      child: Row(
        children: wrappedViews,
      ),
    );
  }

  /// Video layout wrapper
  Widget _viewRows(CallNotifier notifier) {
    final views = _getRenderViews(notifier);
    switch (views.length) {
      case 1:
        return Container(
            child: Column(
          children: <Widget>[_videoView(views[0])],
        ));
      case 2:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow([views[0]]),
            _expandedVideoRow([views[1]])
          ],
        ));
      case 3:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow(views.sublist(0, 2)),
            _expandedVideoRow(views.sublist(2, 3))
          ],
        ));
      case 4:
        return Container(
            child: Column(
          children: <Widget>[
            _expandedVideoRow(views.sublist(0, 2)),
            _expandedVideoRow(views.sublist(2, 4))
          ],
        ));
      default:
    }
    return Container();
  }

  // Toolbar layout
  Widget _toolbar(CallNotifier notifier) {
    //if (widget.role == ClientRole.Audience) return Container();
    return Container(
      alignment: Alignment.bottomCenter,
      padding: const EdgeInsets.symmetric(vertical: 48),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RawMaterialButton(
            onPressed: () => _onToggleMute(notifier),
            child: Icon(
              notifier.isMuted ? Icons.mic_off : Icons.mic,
              color: notifier.isMuted ? Colors.white : Colors.teal,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: notifier.isMuted ? Colors.teal : Colors.white,
            padding: const EdgeInsets.all(12.0),
          ),
          RawMaterialButton(
            onPressed: () => _onCallEnd(context),
            child: Icon(
              Icons.call_end,
              color: Colors.white,
              size: 35.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.redAccent,
            padding: const EdgeInsets.all(15.0),
          ),
          RawMaterialButton(
            onPressed: () => _onSwitchCamera(notifier),
            child: Icon(
              Icons.switch_camera,
              color: Colors.teal,
              size: 20.0,
            ),
            shape: CircleBorder(),
            elevation: 2.0,
            fillColor: Colors.white,
            padding: const EdgeInsets.all(12.0),
          )
        ],
      ),
    );
  }

  /// Info panel to show logs
  Widget _panel(CallNotifier notifier) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 48),
      alignment: Alignment.bottomCenter,
      child: FractionallySizedBox(
        heightFactor: 0.5,
        child: Container(
          padding: const EdgeInsets.symmetric(vertical: 48),
          child: ListView.builder(
            reverse: true,
            itemCount: notifier.infoStrings.length,
            itemBuilder: (BuildContext context, int index) {
              if (notifier.infoStrings.isEmpty) {
                return null;
              }
              return Padding(
                padding: const EdgeInsets.symmetric(
                  vertical: 3,
                  horizontal: 10,
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Flexible(
                      child: Container(
                        padding: const EdgeInsets.symmetric(
                          vertical: 2,
                          horizontal: 5,
                        ),
                        decoration: BoxDecoration(
                          color: Colors.yellowAccent,
                          borderRadius: BorderRadius.circular(5),
                        ),
                        child: Text(
                          notifier.infoStrings[index],
                          style: TextStyle(color: Colors.blueGrey),
                        ),
                      ),
                    )
                  ],
                ),
              );
            },
          ),
        ),
      ),
    );
  }

  void _onCallEnd(BuildContext context) {
    Navigator.pop(context);
  }

  void _onToggleMute(CallNotifier notifier) {
    notifier.isMuted = notifier.isMuted;
    notifier.engine.muteLocalAudioStream(notifier.isMuted);
  }

  void _onSwitchCamera(CallNotifier model) {
    model.engine.switchCamera();
  }

  @override
  Widget build(BuildContext context) {
    return BaseWidget<CallNotifier>(
      model: CallNotifier(),
      onModelReady: (model) => model.init(widget.channelName),
      builder: (context, notifier, child) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Video Call'),
          ),
          backgroundColor: Colors.black,
          body: Center(
            child: Stack(
              children: <Widget>[
                _viewRows(notifier),
                _panel(notifier),
                _toolbar(notifier),
              ],
            ),
          ),
        );
      },
    );
  }
}

 

Step 5: Run application

 

Agora flutter video call

 

 

Tags: How to make Video call in Flutter, Agora Video call , Agora SDK Flutter



Related

Play youtube video in flutter

 

 

 

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

2513 Views