logo
On this page

Create a super board


Note
  • This document is applicable to the following platforms: iOS, Android.
  • The ZEGOCLOUD Super Board SDK for Flutter does not support loading dynamic PPT files on Android 8.0 devices.

Basic concepts

  • ZegoExpress-Video SDK: Audio and video interactive SDK of ZEGO, which can provide the real-time signaling transmission capability required by the Super Board. The Super Board SDK must be used together with this SDK.
  • Super Board SDK and ZegoSuperBoard SDK: Both refer to the SDKs that provide the ZEGO Super Board service, that is, ZegoSuperBoard.
  • ZegoSuperBoardView: Whiteboard view used by developers for presentation during code implementation.
  • ZegoSuperBoardSubView: Subset of ZegoSuperBoardView, which is actually created by developers. ZegoSuperBoardView will automatically presents the newly created ZegoSuperBoardSubView or specified ZegoSuperBoardSubView using switchSuperBoardSubView.
  • Common whiteboard: Whiteboard created by specifying the width, height, and number of pages, and drawn by a user in real time on the specified whiteboard canvas.

Prerequisites

  • Go to ZEGOCLOUD Admin Console to create a new project, and get the AppID and AppSign of your project.
  • Contact ZEGOCLOUD Technical Support to enable the file sharing feature.

Prepare the environment

Please ensure that the development environment meets the following technical requirements:

  • Flutter version must be between 1.10.0 and 3.13.7 (inclusive).
  • Depending on the device running the sample code, the relevant technical requirements are as follows:

Please configure the development environment as follows:

  • Android Studio: Go to “Preferences > Plugins”, search for the “Flutter” plugin to download it, and configure the path of the downloaded Flutter SDK in the plugin settings.
  • Visual Studio Code: Search for the “Flutter” extension in the app store and download it. After configuring the Flutter environment in either of the above development environments, run flutter doctor in the terminal and follow the prompts to install any missing dependencies.

(optional) Create a new project

Note

If you already have a project, you can skip this step.

If you need to know how to create a Flutter project, please refer to the official Flutter documentation Test drive.

Integrate SDK

  1. In your Flutter project, open the "pubspec.yaml" file and add "zego_superboard" and "zego_express_engine" dependencies in the form of "pub".

    ...
    dependencies:
        zego_superboard: 2.5.0+7 # This version number is just an example; for other integrable versions, please refer to the version information (https://pub.dev/packages/zego_superboard/versions) of zego_superboard
        zego_express_engine: 3.10.3-whiteboard
    ...
  2. After adding and saving the file, execute flutter pub get in the terminal.

  3. Import header files

    In your project, import the header files of the zego_superboard SDK and zego_express_engine SDK.

    import 'package:zego_superboard/zego_superboard.dart';
    import 'package:zego_express_engine/zego_express_engine.dart';
  4. Configure the project

Implement the Super Board

Minimal Code

ZEGOCLOUD provides a minimal example code for creating a Super Board, which can be used as a reference for development.

To run this minimal code, you need to:

  1. Create a "zego_superboard_page.dart" file and copy the following code into the corresponding file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:zego_superboard/zego_superboard.dart';
import 'package:zego_superboard/zego_superboard_defines.dart';
import 'package:zego_express_engine/zego_express_engine.dart';
import 'package:zego_superboard/zego_superboard_event_handler.dart';

class ZegoSuperboardPage extends StatefulWidget {
  const ZegoSuperboardPage({super.key});

  @override
  State<ZegoSuperboardPage> createState() => _ZegoSuperboardPageState();
}

class _ZegoSuperboardPageState extends State<ZegoSuperboardPage> {
  Widget? _previewViewWidget;
  late int currentViewID = -1;
  String userID = DateTime.now().millisecondsSinceEpoch.toString();
  String userName = "zego";
  String roomID = "567";
  int kAppID = ;  // Your AppID
  String kAppSign = "";  // Your AppSign

  ZegoSuperBoardManagerCreateResult? result;
  @override
  void initState() {
    super.initState();
    _initExpressSDK();
    _registerEventHandler();
  }

  /// Initialize RTC
  void _initExpressSDK() async {
    ZegoEngineProfile profile = ZegoEngineProfile(kAppID, ZegoScenario.Default,
        enablePlatformView: false, appSign: kAppSign);
    await ZegoExpressEngine.createEngineWithProfile(profile);
    _initSuperBoardSDK();
  }

  /// Initialize Super Board
  void _initSuperBoardSDK() async {
    ZegoSuperBoardInitConfig boardInitConfig =
        ZegoSuperBoardInitConfig(kAppID, kAppSign, userID: userID);
    // Enable file alpha environment
    ZegoSuperBoardManager.instance
        .setCustomizedConfig(key: 'set_alpha_env', value: 'true');
    ZegoSuperBoardError boardError =
        await ZegoSuperBoardManager.instance.initWithConfig(boardInitConfig);
    _createCanvasView();
    if (boardError.errorCode == 0) {
      print('Super Board initialized successfully');
    } else {
      print('Super Board initialization failed');
    }
  }

  void _createCanvasView() async {
    await ZegoSuperBoardManager.instance.createCanvasView((viewID) {
      currentViewID = viewID;
    }).then((widget) {
      setState(() {
        _previewViewWidget = widget;
      });
    });
  }

  /// Register event listeners
  void _registerEventHandler() {
    ZegoSuperBoardEventHandler.onError = ((errorCodde) {});

    ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewAdded =
        ((subViewModel) {
      if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewAdded]:$subViewModel');
      }
    });

    ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewRemoved =
        ((subViewModel) async {
      if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewRemoved]:$subViewModel');
      }
    });

    ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewSwitched = ((uniqueID) {
      if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewSwitched]:$uniqueID');
      }
    });

    ZegoSuperBoardEventHandler.onRemoteSuperBoardAuthChanged = ((authInfo) {
      if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardAuthChanged]:$authInfo');
      }
    });

    ZegoSuperBoardEventHandler.onRemoteSuperBoardGraphicAuthChanged =
        ((authInfo) {
      if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardGraphicAuthChanged]:$authInfo');
      }
    });

    ZegoSuperBoardEventHandler.onSuperBoardSubViewScrollChanged =
        ((uniqueID, page, pageCount) {
      if (kDebugMode) {
        print(
            '[Flutter][onRemoteSuperBoardGraphicAuthChanged] uniqueID:$uniqueID, uniqueID:$page');
      }
    });
    ZegoSuperBoardEventHandler.onSuperBoardSubViewSizeChanged =
        ((uniqueID, size) {
      if (kDebugMode) {
        print(
            '[Flutter][onSuperBoardSubViewSizeChanged] uniqueID:$uniqueID, size:$size');
      }
    });

    ZegoSuperBoardEventHandler.onStepChange = (() async {
      if (kDebugMode) {
        print('[Flutter][onStepChange]');
      }
    });

    ZegoSuperBoardEventHandler.uploadFile = ((info) async {
      if (kDebugMode) {
        print('[Flutter][uploadFile] info:$info');
      }
      if (info['state'] == ZegoSuperBoardUploadFileState.upload.value) {
        if (kDebugMode) {
          print(
              '[Flutter][uploadFile] upload_percent:${info['upload_percent']}');
        }
      } else {
        Map? infoMap = info['infoMap'] as Map;
      }
    });

    ZegoSuperBoardEventHandler.uploadH5File = ((info) async {
      if (kDebugMode) {
        print('[Flutter][uploadH5File] info:$info');
      }
      if (info['state'] == ZegoSuperBoardUploadFileState.upload.value) {
        if (kDebugMode) {
          print(
              '[Flutter][uploadH5File] upload_percent:${info['upload_percent']}');
        }
      } else {
        Map? infoMap = info['infoMap'] as Map;
      }
    });

    ZegoSuperBoardEventHandler.cacheFile = ((info) {
      if (kDebugMode) {
        print('[Flutter][cacheFile] info:$info');
      }
      if (info['state'] == ZegoSuperBoardCacheFileState.caching.value) {}
    });
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Container(
            color: Colors.white,
            child: Column(
              children: [
                Container(
                  color: Colors.grey.withOpacity(0.1),
                  width: double.infinity,
                  height: 248,
                  child: _previewViewWidget ?? Container(),
                ),
                const SizedBox(height: 10),
                Expanded(
                  child: Column(
                    children: [
                      TextButton(
                          onPressed: _loginRoom,
                          child: const Text(
                            'Login to Room',
                            style: TextStyle(
                                fontSize: 18,
                                color: Colors.black,
                                fontWeight: FontWeight.bold),
                          )),
                      TextButton(
                          onPressed: _createWhiteboardView,
                          child: const Text(
                            'Create Pure Whiteboard',
                            style: TextStyle(
                                fontSize: 18,
                                color: Colors.black,
                                fontWeight: FontWeight.bold),
                          )),
                      TextButton(
                        onPressed: _destroySuperBoardSubView,
                        child: const Text(
                          'Destroy Current Super Board',
                          style: TextStyle(
                              fontSize: 18,
                              color: Colors.black,
                              fontWeight: FontWeight.bold),
                        ),
                      ),
                    ],
                  ),
                )
              ],
            )));
  }

  /// Login to room
  void _loginRoom() async {
    ZegoUser user = ZegoUser(userID, userName);
    ZegoRoomConfig config = ZegoRoomConfig.defaultConfig();
    config.isUserStatusNotify = true;
    ZegoRoomLoginResult result = await ZegoExpressEngine.instance
        .loginRoom(roomID, user, config: config);
    if (kDebugMode) {
      print(
          '[Flutter][loginRoom] result: $result  errorCode: ${result.errorCode}');
    }
  }

  /// Create pure whiteboard
  void _createWhiteboardView() async {
    ZegoCreateWhiteboardConfig whiteboardConfig = ZegoCreateWhiteboardConfig(
        'Create Pure Whiteboard',
        perPageWidth: 16,
        perPageHeight: 9,
        pageCount: 5);

    result = await ZegoSuperBoardManager.instance
        .createWhiteboardView(config: whiteboardConfig);
    if (kDebugMode) {
      print(
          '[Flutter][createWhiteboardView] result: $result  errorCode: ${result!.boardError.errorCode}');
    }
  }

  /// Destroy whiteboard
  void _destroySuperBoardSubView() async {
    if (result != null) {
      ZegoSuperBoardManager.instance
          .destroySuperBoardSubView(result!.model.uniqueID);
    }
  }
}
  1. In the "main.dart" file, import the created route ZegoSuperboardPage.
...
Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Superboard view flutter app'),
        ),
        body: const ZegoSuperboardPage(),
      ),
    );
  }
...

1. Initialize the SDK

Initialize the ZEGO Express Video SDK

Call the createEngineWithProfile interface of Express Video SDK and pass in the applied AppID and AppSign Parameters "appID" and "appSign" create an engine singleton object.

int appID = ;  // Your appid
String appSign = '';  // Your appSign
ZegoScenario scenario = ZegoScenario.Default;
ZegoEngineProfile profile = ZegoEngineProfile(appID, scenario, enablePlatformView: false, appSign: appSign);
await ZegoExpressEngine.createEngineWithProfile(profile);

Initialize ZegoSuperBoard SDK

Call the initWithConfig method to initialize the ZegoSuperBoard SDK.

If the return value is 0, it means the initialization is successful, and you can perform more operations. For the errorCode, you can refer to Error codes.

/**
 * appID: The application ID issued by ZEGOCLOUD for developers. Please apply for it from the ZEGO console at <https://console.zego.im>.
 * appSign: The application appSign issued by ZEGOCLOUD for developers. Please apply for it from the ZEGO console at <https://console.zego.im>.
 */

// Create an initialization configuration class
String userID = 'userid';
ZegoSuperBoardInitConfig boardInitConfig = ZegoSuperBoardInitConfig(appID, appSign, userID: userID);
ZegoSuperBoardError boardError = await ZegoSuperBoardManager.instance.initWithConfig(boardInitConfig);
Note

Please initialize ZegoExpress-Video SDK and ZegoSuperBoard SDK successfully before calling other methods.

2. Listen for event callbacks

Based on the actual application needs, you can listen for the concerned event callbacks after the ZegoSuperBoard SDK is initialized. The callbacks include error reminders, adding whiteboard files remotely, deleting whiteboard files remotely, and switching whiteboard files remotely.

Note

The SuperBoard automatically realizes the multi-end synchronization capability, and only needs to refresh the local UI logic in the remote notification callback.

ZegoSuperBoardEventHandler.onError = ((errorCodde) {});
ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewAdded = ((subViewModel) {
    if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewAdded]:$subViewModel');
    }
});
ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewRemoved = ((subViewModel) {
    if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewRemoved]:$subViewModel');
    }
});
ZegoSuperBoardEventHandler.onRemoteSuperBoardSubViewSwitched = ((uniqueID) {
    if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardSubViewSwitched]:$uniqueID');
    }
});
ZegoSuperBoardEventHandler.onRemoteSuperBoardAuthChanged = ((authInfo) {
    if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardAuthChanged]:$authInfo');
    }
});
ZegoSuperBoardEventHandler.onRemoteSuperBoardGraphicAuthChanged = ((authInfo) {
    if (kDebugMode) {
        print('[Flutter][onRemoteSuperBoardGraphicAuthChanged]:$authInfo');
    }
});
ZegoSuperBoardEventHandler.onSuperBoardSubViewScrollChanged = ((uniqueID, page, pageCount) {
    if (kDebugMode) {
        print(
            '[Flutter][onRemoteSuperBoardGraphicAuthChanged] uniqueID:$uniqueID, uniqueID:$page');
    }
});
ZegoSuperBoardEventHandler.onSuperBoardSubViewSizeChanged = ((uniqueID, size) {
    if (kDebugMode) {
        print(
            '[Flutter][onSuperBoardSubViewSizeChanged] uniqueID:$uniqueID, size:$size');
    }
});

3. Log in to the room

You can call the loginRoom interface of ZegoExpressEngine to log in to the room. If the room does not exist, the room will be created and logged in when this interface is called.

The parameters of roomID and user are generated locally by you, but they need to meet the following conditions::

  • Within the same AppID, "roomID" must be globally unique.
  • Within the same AppID, "userID" must be globally unique. It is recommended that developers associate "userID" with their own business account system.
  • "userID" and "userName" cannot be "nil", otherwise it will cause the room login to fail.
  • The constructor of ZegoUser will set the userName to be the same as the parameter userID you passed.
void loginRoom() {
  // The "roomID" is generated locally and must be globally unique. Different users need to log in to the same room to communicate.
  String roomID = "room1";
  // Create a user object. The constructor `userWithUserID` of ZegoUser will set "userName" to be the same as the passed parameter "userID". "userID" and "userName" cannot be "nil", otherwise it will cause the room login to fail.
  // The "userID" is generated locally and must be globally unique.
  ZegoUser user = ZegoUser('user1', 'userName');
  // Only when a ZegoRoomConfig with the "isUserStatusNotify" parameter set to "true" is passed, can the onRoomUserUpdate callback be received.
  ZegoRoomConfig config = ZegoRoomConfig.defaultConfig();
  config.isUserStatusNotify = true;
  // Log in to the room
  ZegoExpressEngine.instance
    .loginRoom(roomID, user, config: config)
    .then((result) => {});
}

4. Add a Whiteboard View

Ensure that the ZegoSuperBoard SDK is initialized successfully and that you have logged into a room before calling the createCanvasView interface to obtain the business scenario Widget. This Widget is used to directly add the whiteboard view (ZegoSuperBoardView) to your business scenario view. Example code is as follows:

// Add this Widget to the page's render tree to display the whiteboard view
_containerWidget = await ZegoSuperBoardManager.instance.createCanvasView((viewID){

});

5. Create a whiteboard

Before creating a whiteboard, you need to ensure that the login is successful. It is recommended to call the interface for creating a common whiteboard in the callback of a successful login.

// Before creating a whiteboard, you need to ensure that the login is successful, i.e., the room callback status is ZegoRoomState.Connected
ZegoExpressEngine.onRoomStateUpdate = (String roomID, ZegoRoomState state,
int errorCode, Map<String, dynamic> extendedData) {
    if (state == ZegoRoomState.Connected && errorCode == 0) {
        // This indicates that the login is successful, and you can only create a whiteboard after a successful login
    }
};

A Super Board can be used to create a common whiteboard.

  • Common whiteboard: Whiteboard created by specifying the width, height, and number of pages, and drawn by a user in real time on the specified whiteboard canvas.

At most 50 whiteboards can be created in a room. The whiteboard creation will fail if the room already has 50 whiteboards.

To obtain the current number of whiteboards in a room, call the querySuperBoardSubViewList method.

ZegoCreateWhiteboardConfig whiteboardConfig = ZegoCreateWhiteboardConfig(
    'A test whiteboard',
    perPageWidth: 16,
    perPageHeight: 9,
    pageCount: ZogoManager.instance.pageCount);
ZegoSuperBoardManagerCreateResult? result = await ZegoSuperBoardManager.instance.createWhiteboardView(config: whiteboardConfig);

6. Destroy a whiteboard

If you need to destroy a specific whiteboard, call the destroySuperBoardSubView interface and pass in the uniqueID of that whiteboard to destroy it.

// After destruction, the SDK will automatically display another whiteboard. The displayed whiteboard will be the one before the destroyed whiteboard.
await ZegoSuperBoardManager.instance.destroySuperBoardSubView('uniqueID');

Test Your App

Run the above project on multiple devices and log in with the same room ID. By pressing and moving your finger within the range of the ZegoSuperBoardView on any device, you can see the doodle effect displayed on the ZegoSuperBoardView of each device.

Learn More

So far, you have successfully built a simple Super Board application. Next, you can explore more features of the Super Board through the following documentation:

Previous

SDK downloads

Next

Draw