hmssdk_flutter 0.7.4 hmssdk_flutter: ^0.7.4 copied to clipboard
The Flutter package for 100ms SDK. Video Conferencing infrastructure for a Video first world.
//Dart imports
import 'dart:async';
//Package imports
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hmssdk_flutter_example/common/util/app_color.dart';
import 'package:hmssdk_flutter_example/common/util/utility_function.dart';
import 'package:hmssdk_flutter_example/enum/meeting_flow.dart';
import 'package:hmssdk_flutter_example/hls-streaming/util/hls_title_text.dart';
import 'package:hmssdk_flutter_example/preview/preview_details.dart';
import 'package:hmssdk_flutter_example/qr_code_screen.dart';
import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:uni_links/uni_links.dart';
//Project imports
import './logs/custom_singleton_logger.dart';
bool _initialURILinkHandled = false;
StreamSubscription? _streamSubscription;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
Wakelock.enable();
Provider.debugCheckInvalidValueType = null;
// Get any initial links
final PendingDynamicLinkData? initialLink =
await FirebaseDynamicLinks.instance.getInitialLink();
runZonedGuarded(() => runApp(HMSExampleApp(initialLink: initialLink?.link)),
FirebaseCrashlytics.instance.recordError);
}
class HMSExampleApp extends StatefulWidget {
final Uri? initialLink;
HMSExampleApp({Key? key, this.initialLink}) : super(key: key);
@override
_HMSExampleAppState createState() => _HMSExampleAppState();
static _HMSExampleAppState of(BuildContext context) =>
context.findAncestorStateOfType<_HMSExampleAppState>()!;
}
class _HMSExampleAppState extends State<HMSExampleApp> {
ThemeMode _themeMode = ThemeMode.dark;
Uri? _currentURI;
bool isDarkMode =
WidgetsBinding.instance?.window.platformBrightness == Brightness.dark;
ThemeData _darkTheme = ThemeData(
brightness: Brightness.dark,
primaryColor: Color.fromARGB(255, 13, 107, 184),
backgroundColor: Colors.black,
scaffoldBackgroundColor: Colors.black);
ThemeData _lightTheme = ThemeData(
primaryColor: Color.fromARGB(255, 13, 107, 184),
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.white,
backgroundColor: Colors.black,
dividerColor: Colors.white54,
);
@override
void initState() {
super.initState();
_initURIHandler();
_incomingLinkHandler();
initDynamicLinks();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
}
Future<void> _initURIHandler() async {
if (!_initialURILinkHandled) {
_initialURILinkHandled = true;
try {
if (widget.initialLink != null) {
return;
}
_currentURI = await getInitialUri();
if (_currentURI != null) {
if (!mounted) {
return;
}
setState(() {});
}
} on PlatformException {
debugPrint("Failed to receive initial uri");
} on FormatException {
if (!mounted) {
return;
}
}
}
}
void _incomingLinkHandler() {
if (!kIsWeb) {
_streamSubscription = uriLinkStream.listen((Uri? uri) {
if (!mounted) {
return;
}
if (uri == null || !uri.toString().contains("100ms.live")) {
return;
}
setState(() {
_currentURI = uri;
});
String tempUri = uri.toString();
if (tempUri.contains("deep_link_id")) {
setState(() {
_currentURI =
Uri.parse(Utilities.fetchMeetingLinkFromFirebase(tempUri));
});
}
}, onError: (Object err) {
if (!mounted) {
return;
}
});
}
}
Future<void> initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink
.listen((PendingDynamicLinkData dynamicLinkData) {
if (!mounted) {
return;
}
if (dynamicLinkData.link.toString().length == 0) {
return;
}
setState(() {
_currentURI = dynamicLinkData.link;
});
}).onError((error) {
print('onLink error');
print(error.message);
});
if (widget.initialLink != null) {
_currentURI = widget.initialLink;
setState(() {});
}
}
@override
void dispose() {
_streamSubscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(
deepLinkURL: _currentURI == null ? null : _currentURI.toString(),
),
theme: _lightTheme,
darkTheme: _darkTheme,
themeMode: _themeMode,
);
}
void changeTheme(ThemeMode themeMode) {
setState(() {
_themeMode = themeMode;
isDarkMode = themeMode == ThemeMode.dark;
updateColor(_themeMode);
});
}
}
class HomePage extends StatefulWidget {
final String? deepLinkURL;
const HomePage({Key? key, this.deepLinkURL}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController meetingLinkController = TextEditingController();
CustomLogger logger = CustomLogger();
bool skipPreview = false;
bool mirrorCamera = true;
bool showStats = false;
List<bool> mode = [true, false]; //0-> meeting ,1 -> HLS mode
PackageInfo _packageInfo = PackageInfo(
appName: 'Unknown',
packageName: 'Unknown',
version: 'Unknown',
buildNumber: 'Unknown',
buildSignature: 'Unknown',
);
@override
void initState() {
super.initState();
logger.getCustomLogger();
_initPackageInfo();
getData();
}
void getData() async {
String savedMeetingUrl = await Utilities.getStringData(key: 'meetingLink');
if (widget.deepLinkURL == null && savedMeetingUrl.isNotEmpty) {
meetingLinkController.text = savedMeetingUrl;
} else {
meetingLinkController.text = widget.deepLinkURL ?? "";
}
int index = await Utilities.getIntData(key: 'mode');
mode[index] = true;
mode[1 - index] = false;
}
Future<bool> _closeApp() {
CustomLogger.file?.delete();
return Future.value(true);
}
Future<void> _initPackageInfo() async {
final info = await PackageInfo.fromPlatform();
setState(() {
_packageInfo = info;
});
}
void handleClick(int value) {
switch (value) {
case 1:
skipPreview = !skipPreview;
break;
case 2:
mirrorCamera = !mirrorCamera;
break;
case 3:
showStats = !showStats;
break;
case 4:
break;
}
}
@override
void didUpdateWidget(covariant HomePage oldWidget) {
if (widget.deepLinkURL != null) {
meetingLinkController.text = widget.deepLinkURL!;
}
super.didUpdateWidget(oldWidget);
}
void joinMeeting() {
if (meetingLinkController.text.isEmpty) {
return;
}
Utilities.saveIntData(key: "mode", value: mode[0] == true ? 0 : 1);
FocusManager.instance.primaryFocus?.unfocus();
Utilities.setRTMPUrl(meetingLinkController.text);
MeetingFlow flow = Utilities.deriveFlow(meetingLinkController.text.trim());
if (flow == MeetingFlow.meeting || flow == MeetingFlow.hlsStreaming) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PreviewDetails(
meetingLink: meetingLinkController.text.trim(),
meetingFlow: flow,
)));
} else {
Utilities.showToast("Please enter valid url");
}
}
@override
Widget build(BuildContext context) {
bool isDarkMode = HMSExampleApp.of(context).isDarkMode;
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return WillPopScope(
onWillPop: _closeApp,
child: SafeArea(
child: Scaffold(
// appBar: AppBar(
// backgroundColor: isDarkMode ? Colors.black : Colors.white,
// elevation: 0,
// title: Text(
// '100ms',
// style: GoogleFonts.inter(color: iconColor),
// ),
// actions: [
// IconButton(
// onPressed: () {
// if (isDarkMode) {
// HMSExampleApp.of(context).changeTheme(ThemeMode.light);
// } else {
// HMSExampleApp.of(context).changeTheme(ThemeMode.dark);
// }
// },
// icon: isDarkMode
// ? SvgPicture.asset(
// 'assets/icons/light_mode.svg',
// color: iconColor,
// )
// : SvgPicture.asset(
// 'assets/icons/dark_mode.svg',
// color: iconColor,
// )),
// PopupMenuButton<int>(
// onSelected: handleClick,
// icon: SvgPicture.asset(
// 'assets/icons/settings.svg',
// color: iconColor,
// ),
// itemBuilder: (BuildContext context) {
// return [
// PopupMenuItem(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// if (skipPreview)
// Text("Enable Preview",
// style: GoogleFonts.inter(color: iconColor))
// else
// Text(
// "Disable Preview",
// style: GoogleFonts.inter(color: Colors.blue),
// ),
// if (skipPreview)
// SvgPicture.asset(
// 'assets/icons/preview_state_on.svg',
// color: iconColor)
// else
// SvgPicture.asset(
// 'assets/icons/preview_state_off.svg',
// color: Colors.blue,
// ),
// ],
// ),
// value: 1,
// ),
// PopupMenuItem(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// if (mirrorCamera)
// Text("Disable Mirroring",
// style: GoogleFonts.inter(color: Colors.blue))
// else
// Text(
// "Enable Mirroring",
// style: GoogleFonts.inter(color: iconColor),
// ),
// Icon(
// Icons.camera_front,
// color: mirrorCamera ? Colors.blue : iconColor,
// ),
// ],
// ),
// value: 2,
// ),
// PopupMenuItem(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// if (showStats)
// Text("Disable Stats",
// style: GoogleFonts.inter(color: Colors.blue))
// else
// Text(
// "Enable Stats",
// style: GoogleFonts.inter(color: iconColor),
// ),
// SvgPicture.asset(
// 'assets/icons/stats.svg',
// color: showStats ? Colors.blue : iconColor,
// ),
// ],
// ),
// value: 3,
// ),
// PopupMenuItem(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text("Version ${_packageInfo.version}",
// style: GoogleFonts.inter(color: iconColor)),
// ],
// ),
// value: 4,
// ),
// ];
// },
// ),
// ],
// ),
body: Center(
child: SingleChildScrollView(
child: Column(
children: [
SvgPicture.asset(
'assets/welcome.svg',
width: width * 0.95,
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Text('Experience the power of 100ms',
textAlign: TextAlign.center,
style: GoogleFonts.inter(
letterSpacing: 0.25,
color: defaultColor,
height: 1.17,
fontSize: 34,
fontWeight: FontWeight.w600)),
),
SizedBox(
height: 8,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 27),
child: Text(
'Jump right in by pasting a room link or scanning a QR code',
textAlign: TextAlign.center,
style: GoogleFonts.inter(
letterSpacing: 0.5,
color: subHeadingColor,
height: 1.5,
fontSize: 16,
fontWeight: FontWeight.w400)),
),
SizedBox(
height: 15,
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text("Joining Link",
key: Key('joining_link_text'),
style: GoogleFonts.inter(
color: defaultColor,
height: 1.5,
fontSize: 14,
fontWeight: FontWeight.w400)),
// ToggleButtons(
// key: Key('mode_toggle_button'),
// selectedColor: hmsdefaultColor,
// selectedBorderColor: hmsdefaultColor,
// borderRadius: BorderRadius.circular(10),
// textStyle: GoogleFonts.inter(
// color: defaultColor,
// fontSize: 12,
// fontWeight: FontWeight.w600),
// children: [Text(" Meeting "), Text("HLS")],
// onPressed: (int index) {
// setState(() {
// for (int buttonIndex = 0;
// buttonIndex < mode.length;
// buttonIndex++) {
// if (buttonIndex == index) {
// mode[buttonIndex] = true;
// } else {
// mode[buttonIndex] = false;
// }
// }
// });
// },
// isSelected: mode)
],
),
),
SizedBox(
width: width * 0.95,
child: TextField(
key: Key('meeting_link_field'),
textInputAction: TextInputAction.done,
onSubmitted: (value) {
joinMeeting();
},
style: GoogleFonts.inter(),
controller: meetingLinkController,
keyboardType: TextInputType.url,
onChanged: (value) {
setState(() {});
},
decoration: InputDecoration(
focusColor: hmsdefaultColor,
contentPadding:
EdgeInsets.symmetric(vertical: 12, horizontal: 16),
fillColor: surfaceColor,
filled: true,
hintText: 'Paste the link here',
hintStyle: GoogleFonts.inter(
color: hintColor,
height: 1.5,
fontSize: 16,
fontWeight: FontWeight.w400),
suffixIcon: meetingLinkController.text.isEmpty
? null
: IconButton(
onPressed: () {
meetingLinkController.text = "";
setState(() {});
},
icon: Icon(Icons.clear),
),
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: borderColor, width: 1),
borderRadius: BorderRadius.all(Radius.circular(8))),
border: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(8)))),
),
),
SizedBox(
height: 16,
),
SizedBox(
width: width * 0.95,
child: ValueListenableBuilder<TextEditingValue>(
valueListenable: meetingLinkController,
builder: (context, value, child) {
return ElevatedButton(
style: ButtonStyle(
shadowColor:
MaterialStateProperty.all(surfaceColor),
backgroundColor: meetingLinkController
.text.isEmpty
? MaterialStateProperty.all(surfaceColor)
: MaterialStateProperty.all(hmsdefaultColor),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
))),
onPressed: () async {
joinMeeting();
},
child: Container(
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(8))),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
HLSTitleText(
key: Key('join_now'),
text: 'Join Now',
textColor: meetingLinkController.text.isEmpty
? disabledTextColor
: enabledTextColor,
)
],
),
),
);
}),
),
SizedBox(
height: 20,
),
SizedBox(
width: width * 0.95,
child: Divider(
height: 5,
color: dividerColor,
)),
SizedBox(
height: 20,
),
SizedBox(
width: width * 0.95,
child: ElevatedButton(
style: ButtonStyle(
shadowColor: MaterialStateProperty.all(hmsdefaultColor),
backgroundColor:
MaterialStateProperty.all(hmsdefaultColor),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
))),
onPressed: () async {
bool res = await Utilities.getCameraPermissions();
if (res) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => QRCodeScreen(
meetingFlow: mode[0]
? MeetingFlow.meeting
: MeetingFlow.hlsStreaming,
)));
}
},
child: Container(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8))),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.qr_code,
size: 18,
color: enabledTextColor,
),
SizedBox(
width: 5,
),
HLSTitleText(
key: Key("scan_qr_code"),
text: 'Scan QR Code',
textColor: enabledTextColor)
],
),
),
),
),
],
),
),
)),
),
);
}
}