Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI Cookbook Guides #482

Merged
merged 7 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docusaurus/docs/Flutter/06-ui-cookbook/01-overview.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## What Are UI Cookbooks?
# What Are UI Cookbooks?
Stream UI components are highly customizable and allow you to fully customize styles to your taste. This UI Cookbook will walk you through how to customize each component in your video call.

export const CookbookCard = ({ title, children }) => (
<li
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Permission Requests
During a call, participants can have different capabilities and permissions. This is very common for use cases such as e-learning or large conference calls where the call organizer may want to enable additional controls to prevent users from speaking during large calls or control when users are allowed to broadcast their video.

In this sample, we will take a closer look at the ways in which users can listen, request, and act on permission requests. For more information on the full moderation and permissions of [Stream Video](https://getstream.io/video/), please see our full guide [here](https://getstream.io/video/docs/flutter/permission-and-moderation/).

## Listening for new permission requests
When a user requests a new capability during a call, the `onPermissionRequest` function is triggered on the `Call` object. This function receives an object containing the requested permissions list and an object representing the `CallUser` who made the request.

```dart
@override
void initState() {
super.initState();
/// The [onPermissionRequest] handler can be used to be notified of new requests from users in the call.
/// [CoordinatorCallPermissionRequestEvent] contains the user requesting permissions along with a list
/// containing the different capabilities they would like granted.
widget.call.onPermissionRequest =
(CoordinatorCallPermissionRequestEvent permissionRequestEvent) {
final uid = permissionRequestEvent.user.id;

/// For more complex applications, a user may request one or more permission at the same time. In those cases,
/// the full range of permissions can be retrieved from the `permissions` list.
final permission = permissionRequestEvent.permissions;
};
}
```

The `CoordinatorCallPermissionRequestEvent` also includes:

- `callCid` - ID of the current call
- `createdAt` - Timestamp of when the user made the permission request
- `permissions` - List of call permissions requested by the user
- `user` - Object representing the user making the current request

## Responding to permission requests
Should the call admins wish to grant the request permissions, they can easily do so by calling `grantPermissions` on the `Call` object. The user ID, along with the list of permissions requested, must be supplied to the function.

Below is a simple example of allowing a user to speak during a call:

```dart
/// Once a permission request is received, it can be granted using [grantPermissions] or revoked using [revokePermissions]
Future<void> grantSpeakingPermission(String userID) async {
await widget.call.grantPermissions(
userId: userID,
permissions: [CallPermission.sendAudio],
);
}
```

This workflow can easily be adapted to fit your application’s UI. For example, a very common use case for handling permission requests is to show a modal of all permission requests and a button to either allow or cancel the request.

For a full example, please see our code on [GitHub](https://github.com/GetStream/flutter-video-samples).
120 changes: 120 additions & 0 deletions docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Watching a Livestream

This sample walks you through building advanced UIs for watching a livestream on Flutter.

Check failure on line 3 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L3

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 3, "column": 68}}}, "severity": "ERROR"}

:::note
In this cookbook tutorial, we will assume that you already know how to join a livestream call. If you haven't familiarized yourself with the **[Livestream Tutorial](https://getstream.io/video/docs/flutter/livestreaming/)** yet, we highly recommend doing so before proceeding with this cookbook.

Check failure on line 6 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L6

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 6, "column": 79}}}, "severity": "ERROR"}
::::

To view an HLS stream and benefit from the full experience of selecting different streaming qualities and performing different player actions like muting and unmuting the stream, we recommend using [lecle_yoyo_player](https://pub.dev/packages/lecle_yoyo_player).

Check failure on line 9 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L9

[Vale.Spelling] Did you really mean 'unmuting'?
Raw output
{"message": "[Vale.Spelling] Did you really mean 'unmuting'?", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 9, "column": 159}}}, "severity": "ERROR"}

Check failure on line 9 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L9

[Vale.Spelling] Did you really mean 'lecle_yoyo_player'?
Raw output
{"message": "[Vale.Spelling] Did you really mean 'lecle_yoyo_player'?", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 9, "column": 200}}}, "severity": "ERROR"}

When building for livestreaming, there are a few considerations you need to keep in mind for the UI:

- UI for when the video isn't loaded yet
- A message to show when the livestream hasn't started yet

Check failure on line 14 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L14

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 14, "column": 30}}}, "severity": "ERROR"}
- What to show when the livestream stopped

Check failure on line 15 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L15

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 15, "column": 25}}}, "severity": "ERROR"}
- How to indicate when there are connection problems
- Number of participants
- Duration of the call

## Setting up the UI
To setup our UI for rendering a livestream, we can use Flutter’s built-in `StreamBuilder` widget for listening to changes in our call state and rendering different UIs based on the stage of our live stream:

Check failure on line 21 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L21

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 21, "column": 33}}}, "severity": "ERROR"}

```dart
@override
Widget build(BuildContext context) {
return SafeArea(
child: StreamBuilder(
stream: widget.call.state.valueStream,
initialData: widget.call.state.value,
builder: (context, snapshot) {
final callState = snapshot.data!;
return Stack(
children: [
if (snapshot.hasData && !callState.isBackstage)
// TODO: Render Video

if (snapshot.hasData && !callState.isBackstage)
// TODO: Render Participant Count

if (!snapshot.hasData)
// TODO: Render loading indicator

if (snapshot.hasData && callState.isBackstage)
// TODO: Display message call is not live

],
);
},
),
);
}
}
```

By looking at different properties on our `CallState`, we can render different UIs and messages based on whether the call is live, backstage, or loading. A `Stack` also allows us to add different secondary live stream elements such as a view count above the main UI.

## Rendering the Livestream
Before running the code below to see the livestream in action, we recommend going to our [Dashboard](https://dashboard.getstream.io/) and creating a livestream first since it can be done visually without having to create another instance of the application on the simulator.

Check failure on line 58 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L58

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 58, "column": 42}}}, "severity": "ERROR"}

Check failure on line 58 in docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx

View workflow job for this annotation

GitHub Actions / vale

[vale] docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx#L58

[Vale.Terms] Use 'Livestream' instead of 'livestream'.
Raw output
{"message": "[Vale.Terms] Use 'Livestream' instead of 'livestream'.", "location": {"path": "docusaurus/docs/Flutter/06-ui-cookbook/05-watching-a-livestream.mdx", "range": {"start": {"line": 58, "column": 150}}}, "severity": "ERROR"}

Once a stream is started on the dashboard, the `Livestream ID` can be passed as the `call id` in code.
deven98 marked this conversation as resolved.
Show resolved Hide resolved

![Stream Livestream Dashboard](../assets/cookbook/livestreaming-dashboard.png)

```dart
@override
Widget build(BuildContext context) {
return SafeArea(
child: StreamBuilder(
stream: widget.call.state.valueStream,
initialData: widget.call.state.value,
builder: (context, snapshot) {
final callState = snapshot.data!;
return Stack(
children: [
if (snapshot.hasData && !callState.isBackstage)
_buildRender(callState),
if (snapshot.hasData && !callState.isBackstage)
Positioned(
top: 12.0,
left: 12.0,
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
color: Colors.red,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Viewers: ${callState.callParticipants.length - 1}',
style: const TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
if (snapshot.hasData && (callState.isBackstage))
const Material(
child: Center(
child: Text('Stream not live'),
),
),
if (!snapshot.hasData)
const Center(
child: CircularProgressIndicator(),
),
],
);
},
),
);
}
}
```

To view our full list of Video examples, please check out our Flutter Cookbook on [GitHub](https://github.com/GetStream/flutter-video-samples).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading