diff --git a/example/images/logo-dark.svg b/example/images/logo-dark.svg
new file mode 100644
index 0000000..adfea0e
--- /dev/null
+++ b/example/images/logo-dark.svg
@@ -0,0 +1,16 @@
+
diff --git a/example/lib/pages/connect.dart b/example/lib/pages/connect.dart
index 8483e9e..d336e37 100644
--- a/example/lib/pages/connect.dart
+++ b/example/lib/pages/connect.dart
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
import 'package:livekit_client/livekit_client.dart';
+import 'package:livekit_example/widgets/text_field.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../exts.dart';
@@ -105,74 +107,110 @@ class _ConnectPageState extends State {
@override
Widget build(BuildContext context) => Scaffold(
- appBar: AppBar(
- title: const Text('Connect to LiveKit'),
- ),
- body: Center(
+ body: Container(
+ alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.symmetric(
- vertical: 20,
horizontal: 20,
),
- decoration: BoxDecoration(
- color: Theme.of(context).cardColor,
- borderRadius: BorderRadius.circular(8),
- border:
- Border.all(color: Theme.of(context).colorScheme.secondary),
- ),
- constraints: const BoxConstraints(
- maxWidth: 320,
- ),
+ constraints: const BoxConstraints(maxWidth: 400),
child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextField(
- enableSuggestions: false,
- autocorrect: false,
- controller: _uriCtrl,
- decoration: const InputDecoration(labelText: 'URL'),
- ),
- TextField(
- enableSuggestions: false,
- autocorrect: false,
- controller: _tokenCtrl,
- decoration: const InputDecoration(labelText: 'Token'),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 20),
- child: CheckboxListTile(
- controlAffinity: ListTileControlAffinity.leading,
- onChanged: (value) => _setSimulcast(value),
- title: const Text('Use Simulcast'),
- value: _simulcast,
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 20),
- child: ElevatedButton(
- onPressed: _busy ? null : () => _connect(context),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (_busy)
- const Padding(
- padding: EdgeInsets.only(right: 10),
- child: SizedBox(
- height: 15,
- width: 15,
- child: CircularProgressIndicator(
- color: Colors.white,
- strokeWidth: 2,
- ),
- ),
- ),
- const Text('Connect'),
- ],
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 70),
+ child: SvgPicture.asset(
+ 'images/logo-dark.svg',
),
),
+
+ Padding(
+ padding: const EdgeInsets.only(bottom: 30),
+ child: LKTextField(
+ label: 'Server URL',
+ ctrl: _uriCtrl,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 50),
+ child: LKTextField(
+ label: 'Token',
+ ctrl: _tokenCtrl,
+ ),
+ ),
+ Container(
+ alignment: Alignment.center,
+ child: ElevatedButton(
+ onPressed: _busy ? null : () => _connect(context),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (_busy)
+ const Padding(
+ padding: EdgeInsets.only(right: 10),
+ child: SizedBox(
+ height: 15,
+ width: 15,
+ child: CircularProgressIndicator(
+ color: Colors.white,
+ strokeWidth: 2,
+ ),
+ ),
+ ),
+ const Text('CONNECT'),
+ ],
+ ),
+ ),
+ ),
+
+ // Container(
+ // padding: const EdgeInsets.symmetric(
+ // vertical: 20,
+ // horizontal: 20,
+ // ),
+ // decoration: BoxDecoration(
+ // color: Theme.of(context).cardColor,
+ // borderRadius: BorderRadius.circular(8),
+ // // border: Border.all(
+ // // color: Theme.of(context).colorScheme.secondary),
+ // ),
+ // constraints: const BoxConstraints(
+ // maxWidth: 320,
+ // ),
+ // child: Column(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [
+ // TextField(
+ // enableSuggestions: false,
+ // autocorrect: false,
+ // controller: _uriCtrl,
+ // decoration: const InputDecoration(labelText: 'URL'),
+ // ),
+ // TextField(
+ // enableSuggestions: false,
+ // autocorrect: false,
+ // controller: _tokenCtrl,
+ // decoration: const InputDecoration(labelText: 'Token'),
+ // ),
+ // Padding(
+ // padding: const EdgeInsets.only(top: 20),
+ // child: CheckboxListTile(
+ // controlAffinity: ListTileControlAffinity.leading,
+ // onChanged: (value) => _setSimulcast(value),
+ // title: const Text('Use Simulcast'),
+ // value: _simulcast,
+ // ),
+ // ),
+ //
+ // ],
+ // ),
+ // ),
+ ]
+ // .map((e) => Padding(
+ // padding: const EdgeInsets.only(bottom: 50), child: e))
+ // .toList(),
),
- ],
- ),
),
),
);
diff --git a/example/lib/pages/room.dart b/example/lib/pages/room.dart
index 6560d32..f5340ae 100644
--- a/example/lib/pages/room.dart
+++ b/example/lib/pages/room.dart
@@ -142,10 +142,9 @@ class _RoomPageState extends State {
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participants.length - 1),
- itemBuilder: (BuildContext context, int index) => Container(
+ itemBuilder: (BuildContext context, int index) => SizedBox(
width: 100,
height: 100,
- padding: const EdgeInsets.all(2),
child: ParticipantWidget(participants[index + 1],
quality: VideoQuality.LOW),
),
diff --git a/example/lib/theme.dart b/example/lib/theme.dart
index 3b34a81..730d2bb 100644
--- a/example/lib/theme.dart
+++ b/example/lib/theme.dart
@@ -7,17 +7,23 @@ import 'package:google_fonts/google_fonts.dart';
// https://github.com/flutter/flutter/issues/55092
// https://github.com/flutter/flutter/issues/39113
//
+
+extension LKColors on Colors {
+ static const lkBlue = Color(0xFF5A8BFF);
+ static const lkDarkBlue = Color(0xFF00153C);
+}
+
class LiveKitTheme {
//
final bgColor = Colors.black;
final textColor = Colors.white;
- final cardColor = const Color(0xFF00163c);
- final accentColor = const Color(0xFF2d6aef);
+ final cardColor = LKColors.lkDarkBlue;
+ final accentColor = LKColors.lkBlue;
ThemeData buildThemeData(BuildContext ctx) => ThemeData(
backgroundColor: bgColor,
// accentColor: accentColor,
- colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue),
+ // colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue),
appBarTheme: AppBarTheme(
backgroundColor: cardColor,
),
@@ -29,6 +35,14 @@ class LiveKitTheme {
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
+ textStyle:
+ MaterialStateProperty.all(GoogleFonts.montserrat(
+ fontSize: 15,
+ )),
+ padding: MaterialStateProperty.all(
+ const EdgeInsets.symmetric(vertical: 20, horizontal: 25)),
+ shape: MaterialStateProperty.all(
+ RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
foregroundColor: MaterialStateProperty.all(Colors.white),
// backgroundColor: MaterialStateProperty.all(accentColor),
backgroundColor: MaterialStateProperty.resolveWith((states) {
@@ -43,8 +57,13 @@ class LiveKitTheme {
checkColor: MaterialStateProperty.all(Colors.white),
fillColor: MaterialStateProperty.all(accentColor),
),
- dialogBackgroundColor: cardColor,
- textTheme: GoogleFonts.latoTextTheme(
+ dialogTheme: DialogTheme(
+ backgroundColor: cardColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ ),
+ ),
+ textTheme: GoogleFonts.montserratTextTheme(
Theme.of(ctx).textTheme,
).apply(
displayColor: textColor,
@@ -53,15 +72,14 @@ class LiveKitTheme {
),
hintColor: Colors.red,
inputDecorationTheme: InputDecorationTheme(
- labelStyle: TextStyle(
- color: textColor.withOpacity(.5),
+ labelStyle: const TextStyle(
+ color: LKColors.lkBlue,
),
- enabledBorder: UnderlineInputBorder(
- borderSide: BorderSide(color: textColor.withOpacity(0.1)),
- ),
- focusedBorder: UnderlineInputBorder(
- borderSide: BorderSide(color: accentColor),
+ hintStyle: TextStyle(
+ color: LKColors.lkBlue.withOpacity(.5),
),
+ enabledBorder: InputBorder.none,
+ focusedBorder: InputBorder.none,
),
);
}
diff --git a/example/lib/widgets/controls.dart b/example/lib/widgets/controls.dart
index 93fec83..1afa36d 100644
--- a/example/lib/widgets/controls.dart
+++ b/example/lib/widgets/controls.dart
@@ -151,56 +151,74 @@ class _ControlsWidgetState extends State {
final videoPub = participant.videoTracks.firstOrNull;
final videoEnabled = videoPub != null && !videoPub.muted;
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- IconButton(
- onPressed: _unpublishAll,
- icon: const Icon(EvaIcons.closeCircleOutline),
- ),
- if (canMute)
+ return Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 15,
+ horizontal: 15,
+ ),
+ child: Wrap(
+ alignment: WrapAlignment.center,
+ spacing: 5,
+ runSpacing: 5,
+ children: [
IconButton(
- onPressed: _muteAudio,
- icon: const Icon(EvaIcons.mic),
- )
- else
- IconButton(
- onPressed: _unmuteAudio,
- icon: const Icon(EvaIcons.micOff),
+ onPressed: _unpublishAll,
+ icon: const Icon(EvaIcons.closeCircleOutline),
+ tooltip: 'Unpublish all',
),
- if (videoEnabled)
+ if (canMute)
+ IconButton(
+ onPressed: _muteAudio,
+ icon: const Icon(EvaIcons.mic),
+ tooltip: 'mute audio',
+ )
+ else
+ IconButton(
+ onPressed: _unmuteAudio,
+ icon: const Icon(EvaIcons.micOff),
+ tooltip: 'un-mute audio',
+ ),
+ if (videoEnabled)
+ IconButton(
+ onPressed: _muteVideo,
+ icon: const Icon(EvaIcons.video),
+ tooltip: 'mute video',
+ )
+ else
+ IconButton(
+ onPressed: _unmuteVideo,
+ icon: const Icon(EvaIcons.videoOff),
+ tooltip: 'un-mute video',
+ ),
IconButton(
- onPressed: _muteVideo,
- icon: const Icon(EvaIcons.video),
- )
- else
- IconButton(
- onPressed: _unmuteVideo,
- icon: const Icon(EvaIcons.videoOff),
+ icon: Icon(position == CameraPosition.back
+ ? EvaIcons.camera
+ : EvaIcons.person),
+ onPressed: () => _toggleCamera(),
+ tooltip: 'toggle camera',
),
- IconButton(
- icon: Icon(position == CameraPosition.back
- ? EvaIcons.camera
- : EvaIcons.person),
- onPressed: () => _toggleCamera(),
- ),
- IconButton(
- icon: const Icon(EvaIcons.monitor),
- onPressed: () => _shareScreen(),
- ),
- IconButton(
- onPressed: _onTapDisconnect,
- icon: const Icon(EvaIcons.closeCircle),
- ),
- IconButton(
- onPressed: _onTapSendData,
- icon: const Icon(EvaIcons.paperPlane),
- ),
- IconButton(
- onPressed: _onTapReconnect,
- icon: const Icon(EvaIcons.refresh),
- ),
- ],
+ IconButton(
+ icon: const Icon(EvaIcons.monitor),
+ onPressed: () => _shareScreen(),
+ tooltip: 'share screen (experimental)',
+ ),
+ IconButton(
+ onPressed: _onTapDisconnect,
+ icon: const Icon(EvaIcons.closeCircle),
+ tooltip: 'disconnect',
+ ),
+ IconButton(
+ onPressed: _onTapSendData,
+ icon: const Icon(EvaIcons.paperPlane),
+ tooltip: 'send demo data',
+ ),
+ IconButton(
+ onPressed: _onTapReconnect,
+ icon: const Icon(EvaIcons.refresh),
+ tooltip: 're-connect',
+ ),
+ ],
+ ),
);
}
}
diff --git a/example/lib/widgets/no_video.dart b/example/lib/widgets/no_video.dart
index 0a4c371..c428794 100644
--- a/example/lib/widgets/no_video.dart
+++ b/example/lib/widgets/no_video.dart
@@ -2,6 +2,8 @@ import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
+import 'package:livekit_example/theme.dart';
+
class NoVideoWidget extends StatelessWidget {
//
const NoVideoWidget({Key? key}) : super(key: key);
@@ -12,7 +14,7 @@ class NoVideoWidget extends StatelessWidget {
child: LayoutBuilder(
builder: (ctx, constraints) => Icon(
EvaIcons.videoOffOutline,
- color: Theme.of(ctx).colorScheme.secondary,
+ color: LKColors.lkBlue,
size: math.min(constraints.maxHeight, constraints.maxWidth) * 0.3,
),
),
diff --git a/example/lib/widgets/participant.dart b/example/lib/widgets/participant.dart
index b6b8ab5..3b3abd9 100644
--- a/example/lib/widgets/participant.dart
+++ b/example/lib/widgets/participant.dart
@@ -3,6 +3,7 @@ import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
+import 'package:livekit_example/theme.dart';
import 'no_video.dart';
import 'participant_info.dart';
@@ -64,7 +65,17 @@ class _ParticipantWidgetState extends State {
@override
Widget build(BuildContext ctx) => Container(
- color: Theme.of(ctx).cardColor,
+ foregroundDecoration: BoxDecoration(
+ border: widget.participant.isSpeaking
+ ? Border.all(
+ width: 5,
+ color: LKColors.lkBlue,
+ )
+ : null,
+ ),
+ decoration: BoxDecoration(
+ color: Theme.of(ctx).cardColor,
+ ),
child: Stack(
children: [
// Video
diff --git a/example/lib/widgets/text_field.dart b/example/lib/widgets/text_field.dart
new file mode 100644
index 0000000..b0479cd
--- /dev/null
+++ b/example/lib/widgets/text_field.dart
@@ -0,0 +1,48 @@
+import 'package:flutter/material.dart';
+
+class LKTextField extends StatelessWidget {
+ final String label;
+ final TextEditingController? ctrl;
+ const LKTextField({
+ required this.label,
+ this.ctrl,
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 10),
+ child: Text(
+ label,
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ Container(
+ padding: const EdgeInsets.symmetric(
+ vertical: 15,
+ horizontal: 15,
+ ),
+ decoration: BoxDecoration(
+ border: Border.all(
+ width: 1,
+ color: Colors.white.withOpacity(.3),
+ ),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: TextField(
+ controller: ctrl,
+ decoration: const InputDecoration.collapsed(
+ hintText: '',
+ ),
+ keyboardType: TextInputType.url,
+ ),
+ ),
+ ],
+ );
+}
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
index 7af7d96..35d8ad6 100644
--- a/example/macos/Podfile.lock
+++ b/example/macos/Podfile.lock
@@ -1,5 +1,5 @@
PODS:
- - flutter_webrtc (0.2.2):
+ - flutter_webrtc (0.2.3):
- FlutterMacOS
- WebRTC-SDK (= 92.4515.10)
- FlutterMacOS (1.0.0)
@@ -30,7 +30,7 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
SPEC CHECKSUMS:
- flutter_webrtc: 43d8f66a0782f8a79fad49115845b55060725b2a
+ flutter_webrtc: 8d69a6819b422e5caf1c7b2844a05bc6a62b437b
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
shared_preferences_macos: 480ce071d0666e37cef23fe6c702293a3d21799e
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 2dd34ab..9bcba69 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -97,6 +97,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
+ flutter_svg:
+ dependency: "direct main"
+ description:
+ name: flutter_svg
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.23.0+1"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -191,6 +198,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
+ path_drawing:
+ dependency: transitive
+ description:
+ name: path_drawing
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.5.1+1"
+ path_parsing:
+ dependency: transitive
+ description:
+ name: path_parsing
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.1"
path_provider:
dependency: transitive
description:
@@ -226,6 +247,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "4.4.0"
platform:
dependency: transitive
description:
@@ -392,6 +420,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.3.1"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 04a4ed2..e03b2b1 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -13,9 +13,11 @@ dependencies:
provider: ^5.0.0
logging: ^1.0.1
+ shared_preferences: ^2.0.7
+
google_fonts: ^2.1.0
eva_icons_flutter: ^3.0.0
- shared_preferences: ^2.0.7
+ flutter_svg: ^0.23.0+1
livekit_client:
path: ../
@@ -27,39 +29,6 @@ dev_dependencies:
# The following section is specific to Flutter.
flutter:
-
- # The following line ensures that the Material Icons font is
- # included with your application, so that you can use the icons in
- # the material Icons class.
uses-material-design: true
-
- # To add assets to your application, add an assets section, like this:
- # assets:
- # - images/a_dot_burr.jpeg
- # - images/a_dot_ham.jpeg
-
- # An image asset can refer to one or more resolution-specific "variants", see
- # https://flutter.dev/assets-and-images/#resolution-aware.
-
- # For details regarding adding assets from package dependencies, see
- # https://flutter.dev/assets-and-images/#from-packages
-
- # To add custom fonts to your application, add a fonts section here,
- # in this "flutter" section. Each entry in this list should have a
- # "family" key with the font family name, and a "fonts" key with a
- # list giving the asset and other descriptors for the font. For
- # example:
- # fonts:
- # - family: Schyler
- # fonts:
- # - asset: fonts/Schyler-Regular.ttf
- # - asset: fonts/Schyler-Italic.ttf
- # style: italic
- # - family: Trajan Pro
- # fonts:
- # - asset: fonts/TrajanPro.ttf
- # - asset: fonts/TrajanPro_Bold.ttf
- # weight: 700
- #
- # For details regarding fonts from package dependencies,
- # see https://flutter.dev/custom-fonts/#from-packages
+ assets:
+ - images/logo-dark.svg