Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion packages/stream_video_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- Added `CallControlBarThemeData` on `StreamVideoTheme`, and `CallControlBarTheme` to restyle the bar over a subtree.
- Added `StreamDeviceAvailability.enumerationFailed`, which tells an enumeration the platform refused from one that found no device.
- Added `CallParticipantState.trackEnabled`, null for a track nothing has reported, and `TrackOption.wantsOn` for the intent a call was joined with.
- `CallButtonBadge` is no longer exported. It exists so the badge sits in the same place on both call buttons, which is an implementation detail; exporting it committed the package to its shape and gave integrators a way to badge things inconsistently.
- Added `StreamCallButtonBadge`, the badge on the call control buttons, with `StreamCallButtonBadgeTheme`, `StreamCallButtonBadgeThemeData` and `StreamCallButtonBadgeStyle` served from `StreamVideoTheme.callButtonBadgeTheme`.
- `CallControlButton` no longer overrides `StreamButtonTheme` for every tone. Only `positive` repaints the primary background — there is no success button style in the design system — and wrapping the other two overrode an app's own primary style for buttons that never use it.
- `StreamMenuHandle` is an `abstract interface class`, so it cannot be accidentally extended.
- Added `hasNoOptions` on an `Iterable<StreamMenuSection>`, and `StreamAdaptiveMenuAnchor` now drops a heading with no rows under it. Every caller had to know both — "is there anything to open" was recomputed at each of two call sites, and an empty section drew a label over nothing.
Expand Down Expand Up @@ -152,6 +152,11 @@
Every component follows the same shape: `StreamX` resolves the registered builder and falls back to `DefaultX`, which holds the default implementation. The parameters of `StreamX` are carried in a `StreamXProps`, exposed as `StreamX.props`, so a custom builder can read them and `copyWith` them to decorate the default rather than reimplement it.
- Added `StreamParticipantTile`, the participant tile as a replaceable component: register a `participantTile` builder to replace it, or use `DefaultStreamParticipantTile` for the default implementation.

### 🔄 Changed

- The badge on the call control buttons is amber with no border, where it used to be red with one.
- `accentWarning` is a lighter amber, which also repaints the fair bars on `StreamConnectionQualityIndicator`.

### 🐞 Fixed

- Fixed the participant grid rearranging itself when a participant nobody can see starts speaking. They take the place of the tile with the least claim to one — the last one on screen — instead of the first, which used to move every tile below it down one.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';

import '../../stream_video_flutter.dart';
import 'call_button_badge.dart';

/// The tone of a [CallControlButton].
///
Expand Down Expand Up @@ -73,7 +72,7 @@ class CallControlButton extends StatelessWidget {

@override
Widget build(BuildContext context) {
final button = CallButtonBadge(
final button = StreamCallButtonBadge(
showErrorBadge: showErrorBadge,
child: StreamButton.icon(
icon: icon,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';

import '../../stream_video_flutter.dart';
import 'call_button_badge.dart';

/// The colour a [CallFeatureButton] takes while it is on.
///
Expand Down Expand Up @@ -76,7 +75,7 @@ class CallFeatureButton extends StatelessWidget {

@override
Widget build(BuildContext context) {
return CallButtonBadge(
return StreamCallButtonBadge(
showErrorBadge: showErrorBadge,
child: StreamButton.icon(
icon: icon,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import 'package:stream_core_flutter/video.dart';

import '../../stream_video_flutter.dart';
import '../l10n/localization_extension.dart';
import 'call_button_badge.dart';

/// Turns the microphone on and off, with a caret that picks which microphone
/// and speaker to use.
Expand Down Expand Up @@ -408,7 +407,7 @@ class _DeviceSplitButton extends StatelessWidget {
// the leading half can be pressed is the caller's to say — a failed
// open is worth retrying — while the caret follows what it has to
// offer, which is nothing when the platform named no device.
builder: (context, handle) => CallButtonBadge(
builder: (context, handle) => StreamCallButtonBadge(
showErrorBadge: unavailable,
child: StreamSplitButton.icon(
leadingIcon: Icon(icon),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';

import '../../stream_video_flutter.dart';

/// Overlays an error badge on the top-end corner of a call button.
///
/// Shared by [CallControlButton] and [CallFeatureButton] so the badge sits in
/// the same place on both.
///
/// Styling resolves from [StreamCallButtonBadgeTheme], falling back to the
/// design system's defaults for a call control — see
/// [StreamCallButtonBadgeStyle].
class StreamCallButtonBadge extends StatelessWidget {
/// Creates a new instance of [StreamCallButtonBadge].
const StreamCallButtonBadge({
super.key,
required this.showErrorBadge,
required this.child,
this.style,
});

/// Whether to draw the badge at all.
final bool showErrorBadge;

/// The button to badge.
final Widget child;

/// Overrides for the badge's styling.
///
/// Takes precedence over [StreamCallButtonBadgeTheme] for the properties it
/// sets; the rest still resolve from the theme.
final StreamCallButtonBadgeStyle? style;

@override
Widget build(BuildContext context) {
if (!showErrorBadge) return child;

const defaults = _StreamCallButtonBadgeStyleDefaults();
final themeStyle = StreamCallButtonBadgeTheme.of(context).style;
final effective = themeStyle?.merge(style) ?? style;

final overhang = effective?.overhang ?? defaults.overhang;

return Stack(
// The badge deliberately overhangs the button's box, so the stack must
// not clip it away.
clipBehavior: Clip.none,
children: [
child,
PositionedDirectional(
top: -overhang,
end: -overhang,
child: StreamErrorBadge(
size: effective?.size ?? defaults.size,
style: effective?.badgeStyle ?? defaults.badgeStyle,
showBorder: effective?.showBorder ?? defaults.showBorder,
),
),
],
);
}
}

// Default style values for [StreamCallButtonBadge].
//
// The badge sits on a call control, which is itself often over video, so the
// design system gives it the warning severity rather than the shared error one.
class _StreamCallButtonBadgeStyleDefaults extends StreamCallButtonBadgeStyle {
const _StreamCallButtonBadgeStyleDefaults();

@override
StreamErrorBadgeStyle get badgeStyle => StreamErrorBadgeStyle.warning;

@override
StreamErrorBadgeSize get size => StreamErrorBadgeSize.sm;

@override
bool get showBorder => false;

@override
double get overhang => 4;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import 'package:flutter/widgets.dart';
import 'package:theme_extensions_builder_annotation/theme_extensions_builder_annotation.dart';

import '../../../stream_video_flutter.dart';

part 'call_button_badge_theme.g.theme.dart';

/// Applies a call button badge theme to descendant [StreamCallButtonBadge]
/// widgets.
///
/// Wrap a subtree with [StreamCallButtonBadgeTheme] to override how the badge
/// on a call control reads.
///
/// {@tool snippet}
///
/// Make the badge read as an error rather than a warning:
///
/// ```dart
/// StreamCallButtonBadgeTheme(
/// data: StreamCallButtonBadgeThemeData(
/// style: StreamCallButtonBadgeStyle(
/// badgeStyle: StreamErrorBadgeStyle.error,
/// ),
/// ),
/// child: child,
/// )
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [StreamCallButtonBadgeThemeData], which describes the theme.
/// * [StreamCallButtonBadgeStyle], the visual style it carries.
class StreamCallButtonBadgeTheme extends InheritedTheme {
/// Creates a call button badge theme.
const StreamCallButtonBadgeTheme({
super.key,
required this.data,
required super.child,
});

/// The badge theme data for descendant widgets.
final StreamCallButtonBadgeThemeData data;

/// Returns the [StreamCallButtonBadgeThemeData] merged from local and global
/// themes.
///
/// Local values from the nearest [StreamCallButtonBadgeTheme] ancestor take
/// precedence over the global values from
/// [StreamVideoTheme.callButtonBadgeTheme]. This allows partial overrides:
/// setting only [StreamCallButtonBadgeStyle.size] leaves the severity coming
/// from the global theme.
static StreamCallButtonBadgeThemeData of(BuildContext context) {
final localTheme = context
.dependOnInheritedWidgetOfExactType<StreamCallButtonBadgeTheme>();
return StreamVideoTheme.of(context).callButtonBadgeTheme.merge(
localTheme?.data,
);
}

@override
Widget wrap(BuildContext context, Widget child) {
return StreamCallButtonBadgeTheme(data: data, child: child);
}

@override
bool updateShouldNotify(StreamCallButtonBadgeTheme oldWidget) =>
data != oldWidget.data;
}

/// Theme data for customizing [StreamCallButtonBadge] widgets.
///
/// Wraps a [StreamCallButtonBadgeStyle] so it can be served by
/// [StreamCallButtonBadgeTheme] and slotted into [StreamVideoTheme] alongside
/// the other component theme data classes.
///
/// See also:
///
/// * [StreamCallButtonBadgeStyle], the style embedded here.
/// * [StreamCallButtonBadgeTheme], for overriding it in a subtree.
@themeGen
@immutable
class StreamCallButtonBadgeThemeData with _$StreamCallButtonBadgeThemeData {
/// Creates call button badge theme data.
const StreamCallButtonBadgeThemeData({this.style});

/// Visual styling for the badge.
final StreamCallButtonBadgeStyle? style;

/// Linearly interpolate between two theme data objects.
static StreamCallButtonBadgeThemeData? lerp(
StreamCallButtonBadgeThemeData? a,
StreamCallButtonBadgeThemeData? b,
double t,
) => _$StreamCallButtonBadgeThemeData.lerp(a, b, t);
}

/// Visual styling properties for a [StreamCallButtonBadge].
///
/// The badge is a [StreamErrorBadge] pinned to the top-end corner of a call
/// control. This style selects which badge is drawn and where; its colors come
/// from [StreamErrorBadgeTheme] rather than being repeated here, so an app that
/// wants different badge colors themes [StreamErrorBadge] itself.
///
/// Size is the exception: [StreamCallButtonBadge] always passes it, so it comes
/// from here and [StreamErrorBadgeThemeData.size] never reaches this badge.
/// [StreamErrorBadgeThemeData.border] applies only when [showBorder] is set
/// here.
@themeGen
@immutable
class StreamCallButtonBadgeStyle with _$StreamCallButtonBadgeStyle {
/// Creates a badge style with optional property overrides.
const StreamCallButtonBadgeStyle({
this.badgeStyle,
this.size,
this.showBorder,
this.overhang,
});

/// The severity the badge conveys.
///
/// If null, [StreamCallButtonBadge] uses [StreamErrorBadgeStyle.warning],
/// which the design system specifies for call controls.
final StreamErrorBadgeStyle? badgeStyle;

/// The diameter of the badge.
///
/// If null, [StreamCallButtonBadge] uses [StreamErrorBadgeSize.sm].
final StreamErrorBadgeSize? size;

/// Whether a border is drawn around the badge.
///
/// If null, no border is drawn. Note that [StreamErrorBadge] draws one by
/// default, so this is a departure from the core badge.
final bool? showBorder;

/// How far the badge overhangs the button's top-end corner, in logical
/// pixels.
///
/// Applied upwards and towards the end edge, so the horizontal half follows
/// the text direction. If null, 4.
final double? overhang;

/// Linearly interpolate between two styles.
static StreamCallButtonBadgeStyle? lerp(
StreamCallButtonBadgeStyle? a,
StreamCallButtonBadgeStyle? b,
double t,
) => _$StreamCallButtonBadgeStyle.lerp(a, b, t);
}
Loading