diff --git a/lib/apis/services/auth_service.dart b/lib/apis/services/auth_service.dart index a69622c..329eaab 100644 --- a/lib/apis/services/auth_service.dart +++ b/lib/apis/services/auth_service.dart @@ -46,6 +46,7 @@ class AuthService extends GetxService { setAuthToken = decodedData[StringValues.token]; token = decodedData[StringValues.token]; await getDeviceId(); + await getChannelInfo(); } return token; } @@ -56,6 +57,8 @@ class AuthService extends GetxService { setChannelId = decodedData[StringValues.channelId]; setAgoraUid = decodedData[StringValues.agoraUid].toString(); } + AppUtility.printLog("channelId: $_channelId"); + AppUtility.printLog("agoraUid: $_agoraUid"); } String generateDeviceId() { diff --git a/lib/common/primary_filled_btn.dart b/lib/common/primary_filled_btn.dart index 1a9f3d6..c7012aa 100644 --- a/lib/common/primary_filled_btn.dart +++ b/lib/common/primary_filled_btn.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:video_calling_app/constants/colors.dart'; import 'package:video_calling_app/constants/dimens.dart'; import 'package:video_calling_app/constants/styles.dart'; @@ -42,8 +43,8 @@ class NxFilledButton extends StatelessWidget { padding: padding, constraints: BoxConstraints(maxWidth: Dimens.screenWidth), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(borderRadius ?? Dimens.eight), - color: bgColor ?? Theme.of(context).textTheme.bodyText1!.color, + borderRadius: BorderRadius.circular(borderRadius ?? Dimens.four), + color: bgColor ?? ColorValues.primaryColor, ), child: Row( mainAxisSize: MainAxisSize.min, @@ -55,8 +56,7 @@ class NxFilledButton extends StatelessWidget { label, style: labelStyle ?? AppStyles.style16Bold.copyWith( - color: - labelColor ?? Theme.of(context).scaffoldBackgroundColor, + color: labelColor ?? ColorValues.whiteColor, fontSize: fontSize ?? Dimens.sixTeen, ), ), diff --git a/lib/common/primary_outlined_btn.dart b/lib/common/primary_outlined_btn.dart index 39c7075..b71dadc 100644 --- a/lib/common/primary_outlined_btn.dart +++ b/lib/common/primary_outlined_btn.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:video_calling_app/constants/colors.dart'; import 'package:video_calling_app/constants/dimens.dart'; import 'package:video_calling_app/constants/styles.dart'; @@ -54,11 +55,11 @@ class NxOutlinedButton extends StatelessWidget { ), decoration: BoxDecoration( border: Border.all( - color: borderColor ?? Theme.of(context).textTheme.bodyText1!.color!, - width: borderWidth ?? Dimens.one * 1.5, + color: borderColor ?? ColorValues.primaryColor, + width: borderWidth ?? Dimens.two, style: borderStyle ?? BorderStyle.solid, ), - borderRadius: BorderRadius.circular(borderRadius ?? Dimens.eight), + borderRadius: BorderRadius.circular(borderRadius ?? Dimens.four), color: bgColor ?? Colors.transparent, ), child: Row( @@ -72,8 +73,7 @@ class NxOutlinedButton extends StatelessWidget { label, style: labelStyle ?? AppStyles.style16Bold.copyWith( - color: labelColor ?? - Theme.of(context).textTheme.bodyText1!.color, + color: labelColor ?? ColorValues.primaryColor, fontSize: fontSize ?? Dimens.sixTeen, ), ), diff --git a/lib/helpers/utility.dart b/lib/helpers/utility.dart index 6d399b7..2c10a32 100644 --- a/lib/helpers/utility.dart +++ b/lib/helpers/utility.dart @@ -152,13 +152,14 @@ abstract class AppUtility { Get.showSnackbar( GetSnackBar( margin: EdgeInsets.only( + top: Dimens.sixTeen, left: Dimens.sixTeen, right: Dimens.sixTeen, - bottom: Dimens.thirtyTwo, ), borderRadius: Dimens.four, padding: Dimens.edgeInsets16, snackStyle: SnackStyle.FLOATING, + snackPosition: SnackPosition.TOP, messageText: Text( message, style: TextStyle( @@ -181,7 +182,7 @@ abstract class AppUtility { ), shouldIconPulse: false, backgroundColor: Theme.of(Get.context!).snackBarTheme.backgroundColor!, - duration: Duration(seconds: duration ?? 1), + duration: Duration(seconds: duration ?? 2), ), ); } @@ -207,8 +208,8 @@ abstract class AppUtility { } } - static Future saveLoginDataToLocalStorage(token, expiresAt) async { - if (token!.isNotEmpty && expiresAt!.isNotEmpty) { + static Future saveLoginDataToLocalStorage(token, int expiresAt) async { + if (token!.isNotEmpty && expiresAt > 0) { final data = jsonEncode({ StringValues.token: token, StringValues.expiresAt: expiresAt, @@ -347,8 +348,8 @@ abstract class AppUtility { } /// Generates a random string of [length] with only numeric characters. - static String randomNumeric(int length) => - randomString(length, from: numericStart, to: numericEnd); + static int randomNumeric(int length) => + int.parse(randomString(length, from: numericStart, to: numericEnd)); static int randomIntNumeric(int length) => int.parse(randomString(length, from: numericStart, to: numericEnd)); diff --git a/lib/main.dart b/lib/main.dart index 0fa87ba..0e8ee46 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -36,7 +36,8 @@ Future initServices() async { Get ..put(AppThemeController(), permanent: true) ..put(AuthService(), permanent: true) - ..put(ProfileController(), permanent: true); + ..put(ProfileController(), permanent: true) + ..put(AppUpdateController(), permanent: true); serverHealth = await Get.find().checkServerHealth(); AppUtility.printLog("ServerHealth: $serverHealth"); diff --git a/lib/modules/auth/views/forgot_password_view.dart b/lib/modules/auth/views/forgot_password_view.dart index 26a0536..2bee940 100644 --- a/lib/modules/auth/views/forgot_password_view.dart +++ b/lib/modules/auth/views/forgot_password_view.dart @@ -26,7 +26,7 @@ class ForgotPasswordView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Dimens.boxHeight8, + Dimens.boxHeight16, _buildImageHeader(), Dimens.boxHeight16, _buildForgotPasswordFields(), @@ -72,16 +72,16 @@ class ForgotPasswordView extends StatelessWidget { children: [ Text( StringValues.forgotPassword, - style: AppStyles.style24Bold, + style: AppStyles.style32Bold, ), - Dimens.boxHeight16, + Dimens.boxHeight8, Text( 'We will send an OTP on this email address.', style: AppStyles.style14Normal.copyWith( color: ColorValues.darkGrayColor, ), ), - Dimens.boxHeight16, + Dimens.boxHeight32, TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), @@ -92,7 +92,7 @@ class ForgotPasswordView extends StatelessWidget { ), keyboardType: TextInputType.emailAddress, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -101,6 +101,11 @@ class ForgotPasswordView extends StatelessWidget { controller: logic.emailTextController, onEditingComplete: logic.focusNode.unfocus, ), + Dimens.boxHeight32, + const NxTextButton( + label: StringValues.loginToAccount, + onTap: RouteManagement.goToLoginView, + ), Dimens.boxHeight16, Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -118,22 +123,6 @@ class ForgotPasswordView extends StatelessWidget { ], ), Dimens.boxHeight16, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - StringValues.alreadyHaveAccount, - style: AppStyles.style16Normal, - ), - Dimens.boxWidth4, - const NxTextButton( - label: StringValues.login, - onTap: RouteManagement.goToLoginView, - ), - ], - ), - Dimens.boxHeight16, ], ), ), @@ -145,7 +134,7 @@ class ForgotPasswordView extends StatelessWidget { right: 0, child: NxFilledButton( onTap: () => logic.sendResetPasswordOTP(), - label: StringValues.getOtp, + label: StringValues.getOtp.toUpperCase(), fontSize: Dimens.sixTeen, borderRadius: 0.0, ), diff --git a/lib/modules/auth/views/login_view.dart b/lib/modules/auth/views/login_view.dart index 42bd24c..f763a09 100644 --- a/lib/modules/auth/views/login_view.dart +++ b/lib/modules/auth/views/login_view.dart @@ -27,7 +27,7 @@ class LoginView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Dimens.boxHeight8, + Dimens.boxHeight16, _buildImageHeader(), Dimens.boxHeight16, _buildLoginFields(), @@ -73,9 +73,9 @@ class LoginView extends StatelessWidget { children: [ Text( StringValues.login, - style: AppStyles.style24Bold, + style: AppStyles.style32Bold, ), - Dimens.boxHeight16, + Dimens.boxHeight32, TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), @@ -86,7 +86,7 @@ class LoginView extends StatelessWidget { ), keyboardType: TextInputType.emailAddress, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -95,7 +95,7 @@ class LoginView extends StatelessWidget { controller: logic.emailUnameTextController, onEditingComplete: logic.focusNode.nextFocus, ), - Dimens.boxHeight24, + Dimens.boxHeight16, TextFormField( obscureText: logic.showPassword, decoration: InputDecoration( @@ -115,7 +115,7 @@ class LoginView extends StatelessWidget { ), keyboardType: TextInputType.visiblePassword, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -124,7 +124,7 @@ class LoginView extends StatelessWidget { controller: logic.passwordTextController, onEditingComplete: logic.focusNode.unfocus, ), - Dimens.boxHeight16, + Dimens.boxHeight32, const NxTextButton( label: StringValues.forgotPassword, onTap: RouteManagement.goToForgotPasswordView, @@ -162,7 +162,7 @@ class LoginView extends StatelessWidget { right: 0, child: NxFilledButton( onTap: () => logic.login(), - label: StringValues.login, + label: StringValues.login.toUpperCase(), fontSize: Dimens.sixTeen, borderRadius: 0.0, ), diff --git a/lib/modules/auth/views/register_view.dart b/lib/modules/auth/views/register_view.dart index 5d11a2d..cfb3c0d 100644 --- a/lib/modules/auth/views/register_view.dart +++ b/lib/modules/auth/views/register_view.dart @@ -27,7 +27,7 @@ class RegisterView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Dimens.boxHeight8, + Dimens.boxHeight16, _buildImageHeader(), Dimens.boxHeight16, _buildRegistrationFields(), @@ -76,7 +76,7 @@ class RegisterView extends StatelessWidget { StringValues.register, style: AppStyles.style24Bold, ), - Dimens.boxHeight16, + Dimens.boxHeight32, Row( children: [ Expanded( @@ -90,7 +90,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.name, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -112,7 +112,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.name, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -124,7 +124,7 @@ class RegisterView extends StatelessWidget { ), ], ), - Dimens.boxHeight24, + Dimens.boxHeight16, TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), @@ -135,7 +135,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.emailAddress, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -144,7 +144,7 @@ class RegisterView extends StatelessWidget { controller: logic.emailTextController, onEditingComplete: logic.focusNode.nextFocus, ), - Dimens.boxHeight24, + Dimens.boxHeight16, TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), @@ -155,7 +155,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.text, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -184,7 +184,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.visiblePassword, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -213,7 +213,7 @@ class RegisterView extends StatelessWidget { ), keyboardType: TextInputType.visiblePassword, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -222,7 +222,7 @@ class RegisterView extends StatelessWidget { controller: logic.confirmPasswordTextController, onEditingComplete: logic.focusNode.unfocus, ), - Dimens.boxHeight16, + Dimens.boxHeight32, Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, @@ -250,7 +250,7 @@ class RegisterView extends StatelessWidget { right: 0, child: NxFilledButton( onTap: () => logic.register(), - label: StringValues.register, + label: StringValues.register.toUpperCase(), fontSize: Dimens.sixTeen, borderRadius: 0.0, ), diff --git a/lib/modules/auth/views/reset_password_view.dart b/lib/modules/auth/views/reset_password_view.dart index 5066a80..d674d28 100644 --- a/lib/modules/auth/views/reset_password_view.dart +++ b/lib/modules/auth/views/reset_password_view.dart @@ -28,7 +28,7 @@ class ResetPasswordView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Dimens.boxHeight8, + Dimens.boxHeight16, _buildImageHeader(), Dimens.boxHeight16, _buildResetPasswordFields(), @@ -74,16 +74,16 @@ class ResetPasswordView extends StatelessWidget { children: [ Text( StringValues.resetPassword, - style: AppStyles.style24Bold, + style: AppStyles.style32Bold, ), - Dimens.boxHeight16, + Dimens.boxHeight8, Text( 'Enter the OTP that we sent to your email and create new password.', style: AppStyles.style14Normal.copyWith( color: ColorValues.darkGrayColor, ), ), - Dimens.boxHeight16, + Dimens.boxHeight32, TextFormField( decoration: const InputDecoration( border: OutlineInputBorder(), @@ -98,7 +98,7 @@ class ResetPasswordView extends StatelessWidget { FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(6), ], - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -107,7 +107,7 @@ class ResetPasswordView extends StatelessWidget { controller: logic.otpTextController, onEditingComplete: logic.focusNode.nextFocus, ), - Dimens.boxHeight24, + Dimens.boxHeight16, TextFormField( obscureText: logic.showPassword, decoration: InputDecoration( @@ -127,7 +127,7 @@ class ResetPasswordView extends StatelessWidget { ), keyboardType: TextInputType.visiblePassword, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -136,7 +136,7 @@ class ResetPasswordView extends StatelessWidget { controller: logic.passwordTextController, onEditingComplete: logic.focusNode.nextFocus, ), - Dimens.boxHeight24, + Dimens.boxHeight16, TextFormField( obscureText: logic.showPassword, decoration: InputDecoration( @@ -156,7 +156,7 @@ class ResetPasswordView extends StatelessWidget { ), keyboardType: TextInputType.visiblePassword, maxLines: 1, - style: AppStyles.style16Normal.copyWith( + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! @@ -165,6 +165,11 @@ class ResetPasswordView extends StatelessWidget { controller: logic.confirmPasswordTextController, onEditingComplete: logic.focusNode.unfocus, ), + Dimens.boxHeight32, + const NxTextButton( + label: StringValues.loginToAccount, + onTap: RouteManagement.goToLoginView, + ), Dimens.boxHeight16, Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -193,7 +198,7 @@ class ResetPasswordView extends StatelessWidget { right: 0, child: NxFilledButton( onTap: () => logic.resetPassword(), - label: StringValues.resetPassword, + label: StringValues.resetPassword.toUpperCase(), fontSize: Dimens.sixTeen, borderRadius: 0.0, ), diff --git a/lib/modules/auth/views/send_account_verification_otp_view.dart b/lib/modules/auth/views/send_account_verification_otp_view.dart index d01ffa4..cfa76d6 100644 --- a/lib/modules/auth/views/send_account_verification_otp_view.dart +++ b/lib/modules/auth/views/send_account_verification_otp_view.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:video_calling_app/common/custom_app_bar.dart'; +import 'package:video_calling_app/common/asset_image.dart'; import 'package:video_calling_app/common/primary_filled_btn.dart'; import 'package:video_calling_app/common/primary_text_btn.dart'; import 'package:video_calling_app/constants/colors.dart'; @@ -24,12 +24,11 @@ class SendAccountVerificationOtpView extends StatelessWidget { height: Dimens.screenHeight, child: Column( mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NxAppBar( - title: StringValues.verifyAccount, - padding: Dimens.edgeInsets8_16, - ), + Dimens.boxHeight16, + _buildImageHeader(), + Dimens.boxHeight16, _buildBody(), ], ), @@ -39,90 +38,116 @@ class SendAccountVerificationOtpView extends StatelessWidget { ); } + Widget _buildImageHeader() => Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + NxAssetImage( + imgAsset: AssetValues.appIcon, + maxHeight: Dimens.eighty, + ), + Text( + StringValues.appName, + textAlign: TextAlign.center, + style: AppStyles.style24Bold.copyWith( + color: ColorValues.primaryColor, + ), + ), + ], + ); + Widget _buildBody() => GetBuilder( builder: (logic) => Expanded( - child: SingleChildScrollView( - child: Padding( - padding: Dimens.edgeInsets0_16, - child: FocusScope( - node: logic.focusNode, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Dimens.boxHeight32, - Text( - StringValues.verifyYourAccount, - style: AppStyles.style32Bold.copyWith( - fontWeight: FontWeight.w900, - ), - ), - Dimens.boxHeight4, - Text( - StringValues.enterEmailForOtp, - style: AppStyles.style12Normal, - ), - Dimens.boxHeight32, - Container( - height: Dimens.fiftySix, - constraints: BoxConstraints(maxWidth: Dimens.screenWidth), - child: TextFormField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(Dimens.eight), - ), - hintText: StringValues.enterEmail, - hintStyle: AppStyles.style14Normal.copyWith( - color: ColorValues.grayColor, + child: Stack( + children: [ + SingleChildScrollView( + child: Padding( + padding: Dimens.edgeInsets0_8, + child: FocusScope( + node: logic.focusNode, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + StringValues.verifyYourAccount, + style: AppStyles.style32Bold.copyWith( + fontWeight: FontWeight.w900, ), ), - keyboardType: TextInputType.emailAddress, - maxLines: 1, - style: AppStyles.style14Normal.copyWith( - color: - Theme.of(Get.context!).textTheme.bodyText1!.color, - ), - controller: logic.emailTextController, - onEditingComplete: logic.focusNode.unfocus, - ), - ), - Dimens.boxHeight32, - NxTextButton( - label: StringValues.loginToAccount, - onTap: () { - RouteManagement.goToBack(); - RouteManagement.goToLoginView(); - }, - ), - Dimens.boxHeight32, - NxFilledButton( - onTap: () => logic.sendVerifyAccountOtp(), - label: StringValues.sendOtp.toUpperCase(), - ), - Dimens.boxHeight48, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ + Dimens.boxHeight8, Text( - StringValues.alreadyHaveOtp, - style: AppStyles.style14Normal, + StringValues.enterEmailForOtp, + style: AppStyles.style14Normal.copyWith( + color: ColorValues.darkGrayColor, + ), + ), + Dimens.boxHeight32, + TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: StringValues.enterEmail, + hintStyle: TextStyle( + color: ColorValues.grayColor, + ), + ), + keyboardType: TextInputType.emailAddress, + maxLines: 1, + style: AppStyles.style14Normal.copyWith( + color: Theme.of(Get.context!) + .textTheme + .bodyText1! + .color, + ), + controller: logic.emailTextController, + onEditingComplete: logic.focusNode.unfocus, ), - Dimens.boxWidth4, + Dimens.boxHeight32, NxTextButton( - label: StringValues.verifyAccount, + label: StringValues.loginToAccount, onTap: () { RouteManagement.goToBack(); - RouteManagement.goToVerifyAccountView(); + RouteManagement.goToLoginView(); }, ), + Dimens.boxHeight16, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + StringValues.alreadyHaveOtp, + style: AppStyles.style16Normal, + ), + Dimens.boxWidth4, + NxTextButton( + label: StringValues.verifyAccount, + onTap: () { + RouteManagement.goToBack(); + RouteManagement.goToVerifyAccountView(); + }, + ), + ], + ), + Dimens.boxHeight32, ], ), - Dimens.boxHeight32, - ], + ), ), ), - ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: NxFilledButton( + onTap: () => logic.sendVerifyAccountOtp(), + label: StringValues.sendOtp.toUpperCase(), + fontSize: Dimens.sixTeen, + borderRadius: 0.0, + ), + ), + ], ), ), ); diff --git a/lib/modules/auth/views/verify_account_view.dart b/lib/modules/auth/views/verify_account_view.dart index 14c8284..03f0ea2 100644 --- a/lib/modules/auth/views/verify_account_view.dart +++ b/lib/modules/auth/views/verify_account_view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; -import 'package:video_calling_app/common/custom_app_bar.dart'; +import 'package:video_calling_app/common/asset_image.dart'; import 'package:video_calling_app/common/primary_filled_btn.dart'; import 'package:video_calling_app/common/primary_text_btn.dart'; import 'package:video_calling_app/constants/colors.dart'; @@ -25,12 +25,11 @@ class VerifyAccountView extends StatelessWidget { height: Dimens.screenHeight, child: Column( mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NxAppBar( - title: StringValues.verifyAccount, - padding: Dimens.edgeInsets8_16, - ), + Dimens.boxHeight16, + _buildImageHeader(), + Dimens.boxHeight16, _buildBody(), ], ), @@ -40,121 +39,137 @@ class VerifyAccountView extends StatelessWidget { ); } + Widget _buildImageHeader() => Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + NxAssetImage( + imgAsset: AssetValues.appIcon, + maxHeight: Dimens.eighty, + ), + Text( + StringValues.appName, + textAlign: TextAlign.center, + style: AppStyles.style24Bold.copyWith( + color: ColorValues.primaryColor, + ), + ), + ], + ); + Widget _buildBody() => GetBuilder( builder: (logic) => Expanded( - child: SingleChildScrollView( - child: Padding( - padding: Dimens.edgeInsets0_16, - child: FocusScope( - node: logic.focusNode, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Dimens.boxHeight32, - Text( - StringValues.verifyAccount, - style: AppStyles.style32Bold.copyWith( - fontWeight: FontWeight.w900, - ), - ), - Dimens.boxHeight4, - RichText( - text: TextSpan(children: [ - TextSpan( - text: 'An OTP has been sent to your email address', - style: AppStyles.style12Normal.copyWith( - color: Theme.of(Get.context!) - .textTheme - .bodyText1! - .color, + child: Stack( + children: [ + SingleChildScrollView( + child: Padding( + padding: Dimens.edgeInsets0_8, + child: FocusScope( + node: logic.focusNode, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + StringValues.verifyAccount, + style: AppStyles.style32Bold.copyWith( + fontWeight: FontWeight.w900, ), ), - TextSpan( - text: ' ${logic.emailTextController.text}, ', - style: AppStyles.style12Bold.copyWith( - color: Theme.of(Get.context!) - .textTheme - .bodyText1! - .color, - ), + Dimens.boxHeight8, + RichText( + text: TextSpan(children: [ + TextSpan( + text: + 'An OTP has been sent to your email address', + style: AppStyles.style14Normal.copyWith( + color: ColorValues.darkGrayColor, + ), + ), + TextSpan( + text: ' ${logic.emailTextController.text}, ', + style: AppStyles.style14Bold.copyWith( + color: ColorValues.darkGrayColor, + ), + ), + TextSpan( + text: 'please enter OTP to proceed', + style: AppStyles.style14Normal.copyWith( + color: ColorValues.darkGrayColor, + ), + ), + ]), ), - TextSpan( - text: 'please enter OTP to proceed', - style: AppStyles.style12Normal.copyWith( + Dimens.boxHeight32, + TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintStyle: TextStyle( + color: ColorValues.grayColor, + ), + hintText: StringValues.otp, + ), + keyboardType: TextInputType.number, + maxLines: 1, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(6), + ], + style: AppStyles.style14Normal.copyWith( color: Theme.of(Get.context!) .textTheme .bodyText1! .color, ), + controller: logic.otpTextController, + onEditingComplete: logic.focusNode.unfocus, ), - ]), - ), - Dimens.boxHeight32, - Container( - height: Dimens.fiftySix, - constraints: BoxConstraints(maxWidth: Dimens.screenWidth), - child: TextFormField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(Dimens.eight), - ), - hintStyle: AppStyles.style14Normal.copyWith( - color: ColorValues.grayColor, - ), - hintText: StringValues.otp, - ), - keyboardType: TextInputType.number, - maxLines: 1, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(6), - ], - style: AppStyles.style14Normal.copyWith( - color: - Theme.of(Get.context!).textTheme.bodyText1!.color, - ), - controller: logic.otpTextController, - onEditingComplete: logic.focusNode.nextFocus, - ), - ), - Dimens.boxHeight32, - NxTextButton( - label: StringValues.loginToAccount, - onTap: () { - RouteManagement.goToBack(); - RouteManagement.goToLoginView(); - }, - ), - Dimens.boxHeight32, - NxFilledButton( - onTap: () => logic.verifyAccount(), - label: StringValues.verifyAccount.toUpperCase(), - ), - Dimens.boxHeight48, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - StringValues.doNotHaveOtp, - style: AppStyles.style14Normal, - ), - Dimens.boxWidth4, + Dimens.boxHeight32, NxTextButton( - label: StringValues.getOtp, + label: StringValues.loginToAccount, onTap: () { RouteManagement.goToBack(); - RouteManagement.goToSendVerifyAccountOtpView(); + RouteManagement.goToLoginView(); }, ), + Dimens.boxHeight16, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + StringValues.doNotHaveOtp, + style: AppStyles.style16Normal, + ), + Dimens.boxWidth4, + NxTextButton( + label: StringValues.getOtp, + onTap: () { + RouteManagement.goToBack(); + RouteManagement.goToSendVerifyAccountOtpView(); + }, + ), + ], + ), + Dimens.boxHeight16, ], ), - Dimens.boxHeight32, - ], + ), ), ), - ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: NxFilledButton( + onTap: () => logic.verifyAccount(), + label: StringValues.verifyAccount.toUpperCase(), + fontSize: Dimens.sixTeen, + borderRadius: 0.0, + ), + ), + ], ), ), ); diff --git a/lib/modules/calling/controllers/channel_controller.dart b/lib/modules/calling/controllers/channel_controller.dart index 4214e13..0ad6c32 100644 --- a/lib/modules/calling/controllers/channel_controller.dart +++ b/lib/modules/calling/controllers/channel_controller.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:agora_rtc_engine/rtc_engine.dart'; +import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:video_calling_app/apis/providers/api_provider.dart'; @@ -21,13 +21,15 @@ class ChannelController extends GetxController { final _apiProvider = ApiProvider(http.Client()); - late RtcEngine _rtcEngine; + late final RtcEngine _rtcEngine; + late final RtcEngineEventHandler _eventHandler; - bool _micToggle = false; - bool _cameraToggle = false; + bool _audioMuted = false; + bool _videoMuted = false; bool _isConnecting = false; bool _switchRender = false; bool _showControls = true; + bool _isJoined = false; late String _channelId; @@ -35,9 +37,9 @@ class ChannelController extends GetxController { final List _participants = []; - bool get micToggle => _micToggle; + bool get audioMuted => _audioMuted; - bool get cameraToggle => _cameraToggle; + bool get videoMuted => _videoMuted; bool get isConnecting => _isConnecting; @@ -45,10 +47,16 @@ class ChannelController extends GetxController { bool get showControls => _showControls; + bool get isJoined => _isJoined; + List get participants => _participants; String get channelId => _channelId; + RtcEngine get rtcEngine => _rtcEngine; + + RtcEngineEventHandler get eventHandler => _eventHandler; + Future _getRtcToken() async { AppUtility.printLog("Get RTC Token Request..."); @@ -71,7 +79,6 @@ class ChannelController extends GetxController { AppUtility.showSnackBar( StringValues.internetConnError, StringValues.error); } on TimeoutException { - AppUtility.printLog(StringValues.connTimedOut); AppUtility.printLog(StringValues.connTimedOut); AppUtility.showSnackBar(StringValues.connTimedOut, StringValues.error); } on FormatException catch (e) { @@ -85,91 +92,103 @@ class ChannelController extends GetxController { } } - void _addAgoraEventHandlers() { - _rtcEngine.setEventHandler( - RtcEngineEventHandler( - error: (code) { - final message = 'onError: $code'; - AppUtility.printLog(message); - AppUtility.showSnackBar(message, 'error'); - }, - joinChannelSuccess: (channel, uid, elapsed) { - final message = 'onJoinChannel: $channel, uid: $uid'; - AppUtility.printLog(message); - AppUtility.showSnackBar("You joined.", 'success'); - }, - userJoined: (uid, elapsed) { - final message = 'userJoined: $uid'; - AppUtility.printLog(message); - _participants.add(uid); - update(); - AppUtility.showSnackBar("$uid joined.", 'success'); - }, - userOffline: (uid, elapsed) { - final message = 'userOffline: $uid reason: $elapsed'; - AppUtility.printLog(message); - _participants.removeWhere((element) => element == uid); - update(); - AppUtility.showSnackBar("$uid left.", 'warning'); - }, - leaveChannel: (stats) { - final message = 'leaveChannel ${stats.toJson()}'; - AppUtility.printLog(message); - _participants.clear(); - update(); - }, - connectionLost: () { - const message = "Connection is poor or lost."; - AppUtility.printLog(message); - AppUtility.showSnackBar(message, 'warning'); - }, - connectionInterrupted: () { - const message = "Connection is interrupted."; - AppUtility.printLog(message); - AppUtility.showSnackBar(message, 'warning'); - }, - userMuteAudio: (uid, audioMuted) { - String message; - if (audioMuted) { - message = '$uid muted audio.'; - } else { - message = '$uid unmuted audio.'; - } - AppUtility.printLog(message); - AppUtility.showSnackBar(message, 'warning'); - }, - userMuteVideo: (uid, videoMuted) { - String message; - if (videoMuted) { - message = '$uid muted video.'; - } else { - message = '$uid unmuted video.'; - } - AppUtility.printLog(message); - AppUtility.showSnackBar(message, 'warning'); - }, - ), + void _addAgoraEventHandlers() async { + _eventHandler = RtcEngineEventHandler( + onError: (code, message) { + final message = 'onError: $code'; + AppUtility.printLog(message); + AppUtility.showSnackBar(message, 'error'); + }, + onJoinChannelSuccess: (connection, stats) { + _isJoined = true; + update(); + final message = 'onJoinChannel: $connection, stats: $stats'; + AppUtility.printLog(message); + AppUtility.showSnackBar("You joined.", 'success'); + }, + onUserJoined: (connection, remoteUid, elapsed) { + final message = 'userJoined: $remoteUid'; + AppUtility.printLog(message); + _participants.add(remoteUid); + update(); + AppUtility.showSnackBar("$remoteUid joined.", 'success'); + }, + onUserOffline: (connection, remoteUid, reason) { + final message = 'userOffline: $remoteUid reason: $reason'; + AppUtility.printLog(message); + _participants.removeWhere((element) => element == remoteUid); + update(); + AppUtility.showSnackBar("$remoteUid left.", 'warning'); + }, + onLeaveChannel: (connection, stats) { + final message = 'leaveChannel: $connection stats: ${stats.toJson()}'; + AppUtility.printLog(message); + _participants.clear(); + update(); + }, + onConnectionLost: (connection) { + const message = "Connection is poor or lost."; + AppUtility.printLog(message); + AppUtility.showSnackBar(message, 'warning'); + }, + onConnectionInterrupted: (connection) { + const message = "Connection is interrupted."; + AppUtility.printLog(message); + AppUtility.showSnackBar(message, 'warning'); + }, + onUserMuteAudio: (connection, remoteUid, audioMuted) { + String message; + if (audioMuted) { + message = '$remoteUid muted audio.'; + } else { + message = '$remoteUid unmuted audio.'; + } + AppUtility.printLog(message); + AppUtility.showSnackBar(message, 'warning'); + }, + onUserMuteVideo: (connection, remoteUid, videoMuted) { + String message; + if (videoMuted) { + message = '$remoteUid muted video.'; + } else { + message = '$remoteUid unmuted video.'; + } + AppUtility.printLog(message); + AppUtility.showSnackBar(message, 'warning'); + }, ); + _rtcEngine.registerEventHandler(_eventHandler); } void leaveChannel() async { await _rtcEngine.leaveChannel(); + _rtcEngine.unregisterEventHandler(_eventHandler); update(); RouteManagement.goToBack(); } void toggleMuteVideo() { - _cameraToggle = !_cameraToggle; - _rtcEngine.muteLocalVideoStream(_cameraToggle); + _videoMuted = !_videoMuted; + if (_videoMuted) { + _rtcEngine.disableVideo(); + } else { + _rtcEngine.enableVideo(); + } + _rtcEngine.muteLocalVideoStream(_videoMuted); - AppUtility.printLog("Camera: $_cameraToggle"); + AppUtility.printLog("Camera: $_videoMuted"); update(); } void toggleMuteAudio() { - _micToggle = !_micToggle; - _rtcEngine.muteLocalAudioStream(_micToggle); - AppUtility.printLog("Mic: $_micToggle"); + _audioMuted = !_audioMuted; + if (_audioMuted) { + _rtcEngine.disableAudio(); + } else { + _rtcEngine.enableAudio(); + } + _rtcEngine.muteLocalAudioStream(_audioMuted); + AppUtility.printLog("Mic: $_audioMuted"); update(); } @@ -190,15 +209,24 @@ class ChannelController extends GetxController { Future _initAgoraRtcEngine() async { await Wakelock.enable(); - _rtcEngine = await RtcEngine.create(AppSecrets.appId); + _rtcEngine = createAgoraRtcEngine(); + await _rtcEngine.initialize( + const RtcEngineContext( + appId: AppSecrets.appId, + ), + ); + _addAgoraEventHandlers(); await _rtcEngine.enableVideo(); - await _rtcEngine.setChannelProfile(ChannelProfile.LiveBroadcasting); - await _rtcEngine.setClientRole(ClientRole.Broadcaster); } Future _init() async { + _isConnecting = true; + update(); + + await _initAgoraRtcEngine(); + var cameraPerm = await AppPermissions.checkCameraPermission(); var micPerm = await AppPermissions.checkMicPermission(); @@ -207,16 +235,15 @@ class ChannelController extends GetxController { return; } - _isConnecting = true; - update(); - await _getRtcToken().then((_) async { - await _initAgoraRtcEngine(); await _rtcEngine.joinChannel( - _token, - _channelId, - null, - int.parse(_auth.agoraUid), + token: _token, + channelId: _channelId, + uid: int.parse(_auth.agoraUid), + options: const ChannelMediaOptions( + channelProfile: ChannelProfileType.channelProfileCommunication, + clientRoleType: ClientRoleType.clientRoleBroadcaster, + ), ); _isConnecting = false; update(); @@ -226,18 +253,18 @@ class ChannelController extends GetxController { @override void onInit() { super.onInit(); - _channelId = Get.arguments[0] ?? _auth.channelId; - _cameraToggle = Get.arguments[1] ?? false; - _micToggle = Get.arguments[2] ?? false; - AppUtility.printLog("Mic: $_micToggle"); - AppUtility.printLog("Camera: $_cameraToggle"); + _channelId = Get.arguments[0]?.toString() ?? _auth.channelId.toString(); + _videoMuted = Get.arguments[1] ?? false; + _audioMuted = Get.arguments[2] ?? false; + AppUtility.printLog("Mic: $_audioMuted"); + AppUtility.printLog("Camera: $_videoMuted"); _init(); } @override void onClose() { _rtcEngine.leaveChannel(); - _rtcEngine.destroy(); + _rtcEngine.unregisterEventHandler(_eventHandler); super.onClose(); } } diff --git a/lib/modules/calling/views/calling_view.dart b/lib/modules/calling/views/calling_view.dart index 5f2d0ed..6e209ad 100644 --- a/lib/modules/calling/views/calling_view.dart +++ b/lib/modules/calling/views/calling_view.dart @@ -1,11 +1,15 @@ -import 'package:agora_rtc_engine/rtc_local_view.dart' as rtc_local_view; -import 'package:agora_rtc_engine/rtc_remote_view.dart' as rtc_remote_view; +import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:video_calling_app/common/circular_asset_image.dart'; +import 'package:video_calling_app/common/circular_network_image.dart'; import 'package:video_calling_app/common/primary_icon_btn.dart'; import 'package:video_calling_app/constants/colors.dart'; import 'package:video_calling_app/constants/dimens.dart'; +import 'package:video_calling_app/constants/strings.dart'; +import 'package:video_calling_app/helpers/utility.dart'; import 'package:video_calling_app/modules/calling/controllers/channel_controller.dart'; +import 'package:video_calling_app/modules/profile/controllers/profile_controller.dart'; class CallingView extends StatefulWidget { const CallingView({Key? key}) : super(key: key); @@ -19,16 +23,34 @@ class _CallingViewState extends State @override Widget build(BuildContext context) { super.build(context); - return Scaffold( - body: SafeArea( - child: GetBuilder( - builder: (logic) { - if (logic.isConnecting) { - return const Center(child: CircularProgressIndicator()); - } + var lastExitTime = DateTime.now(); + return WillPopScope( + onWillPop: () async { + if (DateTime.now().difference(lastExitTime) >= + const Duration(seconds: 2)) { + AppUtility.showSnackBar( + 'Press the back button again exit', + '', + duration: 2, + ); + lastExitTime = DateTime.now(); - return _renderVideoOnScreen(logic); - }, + return false; + } else { + return true; + } + }, + child: Scaffold( + body: SafeArea( + child: GetBuilder( + builder: (logic) { + if (!logic.initialized || logic.isConnecting || !logic.isJoined) { + return const Center(child: CircularProgressIndicator()); + } + + return _renderVideoOnScreen(logic); + }, + ), ), ), ); @@ -38,6 +60,7 @@ class _CallingViewState extends State return GestureDetector( onTap: logic.toggleShowControls, child: Stack( + fit: StackFit.expand, children: [ if (logic.participants.isNotEmpty) _renderRemoteVideo(logic), if (logic.participants.length <= 2) _renderLocalVideo(logic), @@ -53,11 +76,14 @@ class _CallingViewState extends State Widget _renderRemoteVideo(ChannelController logic) { if (logic.participants.length == 1) { - return rtc_remote_view.SurfaceView( - uid: logic.participants[0], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + return AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[0]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ); } if (logic.participants.length == 2) { @@ -66,22 +92,28 @@ class _CallingViewState extends State SizedBox( width: Dimens.screenWidth, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[0], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[0]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxHeight4, SizedBox( width: Dimens.screenWidth, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[1], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[1]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ) ], @@ -95,22 +127,28 @@ class _CallingViewState extends State SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[0], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[0]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxWidth4, SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[1], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[1]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ) ], @@ -132,22 +170,28 @@ class _CallingViewState extends State SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[0], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[0]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxWidth4, SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[1], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[1]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), ], @@ -158,11 +202,14 @@ class _CallingViewState extends State SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.fourteen, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[2], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[2]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxWidth4, @@ -183,22 +230,28 @@ class _CallingViewState extends State SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.eighty, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[0], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[0]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxWidth4, SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.eighty, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[1], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[1]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), ], @@ -209,11 +262,14 @@ class _CallingViewState extends State SizedBox( width: (Dimens.screenWidth * 0.5) - Dimens.two, height: (Dimens.screenHeight * 0.5) - Dimens.eighty, - child: rtc_remote_view.SurfaceView( - uid: logic.participants[2], - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: logic.participants[2]), + connection: RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), Dimens.boxWidth4, @@ -239,11 +295,15 @@ class _CallingViewState extends State padding: const EdgeInsets.only(right: 4.0), child: SizedBox( width: Dimens.screenWidth * 0.3, - child: rtc_remote_view.SurfaceView( - uid: uid, - channelId: logic.channelId, - zOrderOnTop: true, - zOrderMediaOverlay: true, + child: AgoraVideoView( + controller: VideoViewController.remote( + rtcEngine: logic.rtcEngine, + canvas: VideoCanvas(uid: uid), + connection: + RtcConnection(channelId: logic.channelId), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), ), ), )) @@ -255,92 +315,139 @@ class _CallingViewState extends State ); } + Widget _buildProfileImage({double? radius}) { + var profile = ProfileController.find; + if (profile.profileData.user != null && + profile.profileData.user!.avatar != null) { + return NxCircleNetworkImage( + imageUrl: profile.profileData.user!.avatar!.url!, + radius: radius ?? Dimens.sixtyFour, + ); + } + return NxCircleAssetImage( + imgAsset: AssetValues.avatar, + radius: radius ?? Dimens.sixtyFour, + ); + } + Widget _renderLocalVideo(ChannelController logic) { if (logic.participants.isEmpty) { return SizedBox( width: Dimens.screenWidth, height: Dimens.screenHeight, - child: const rtc_local_view.SurfaceView( - zOrderOnTop: true, - zOrderMediaOverlay: true, - ), + child: logic.videoMuted + ? Container( + color: Colors.transparent, + width: Dimens.screenWidth, + height: Dimens.screenHeight, + child: Center( + child: _buildProfileImage(), + ), + ) + : AgoraVideoView( + controller: VideoViewController( + rtcEngine: logic.rtcEngine, + canvas: const VideoCanvas(uid: 0), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), + ), ); } if (logic.participants.length > 2) { - return const rtc_local_view.SurfaceView( - zOrderOnTop: true, - zOrderMediaOverlay: true, - ); + return logic.videoMuted + ? Center( + child: _buildProfileImage(radius: Dimens.twenty), + ) + : AgoraVideoView( + controller: VideoViewController( + rtcEngine: logic.rtcEngine, + canvas: const VideoCanvas(uid: 0), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), + ); } return Positioned( - top: 8.0, - right: 8.0, + top: Dimens.eight, + right: Dimens.eight, child: ClipRRect( borderRadius: BorderRadius.circular(Dimens.eight), - child: SizedBox( + child: Container( + color: Theme.of(Get.context!).dialogBackgroundColor, width: Dimens.hundred * 1.2, height: Dimens.hundred * 1.6, - child: const rtc_local_view.SurfaceView( - zOrderOnTop: true, - zOrderMediaOverlay: true, - ), + child: logic.videoMuted + ? Center( + child: _buildProfileImage(radius: Dimens.twenty), + ) + : AgoraVideoView( + controller: VideoViewController( + rtcEngine: logic.rtcEngine, + canvas: const VideoCanvas(uid: 0), + useFlutterTexture: false, + useAndroidSurfaceView: false, + ), + ), ), ), ); } - AnimatedContainer _floatingControlBar(ChannelController logic) => - AnimatedContainer( - color: Colors.transparent, - width: Dimens.screenWidth, - padding: Dimens.edgeInsets16_8, - duration: const Duration(milliseconds: 500), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.min, - children: [ - CircleAvatar( - backgroundColor: !logic.micToggle - ? ColorValues.whiteColor - : ColorValues.errorColor, - radius: Dimens.twentyFour, - child: NxIconButton( - icon: !logic.micToggle ? Icons.mic : Icons.mic_off, - iconColor: !logic.micToggle - ? Theme.of(Get.context!).iconTheme.color - : ColorValues.whiteColor, - onTap: () => logic.toggleMuteAudio(), + Widget _floatingControlBar(ChannelController logic) => GestureDetector( + onTap: () {}, + child: AnimatedContainer( + color: Colors.transparent, + width: Dimens.screenWidth, + padding: Dimens.edgeInsets16, + duration: const Duration(milliseconds: 500), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + CircleAvatar( + backgroundColor: !logic.audioMuted + ? ColorValues.whiteColor + : ColorValues.errorColor, + radius: Dimens.thirtyTwo, + child: NxIconButton( + icon: !logic.audioMuted ? Icons.mic : Icons.mic_off, + iconColor: !logic.audioMuted + ? Theme.of(Get.context!).iconTheme.color + : ColorValues.whiteColor, + onTap: () => logic.toggleMuteAudio(), + ), ), - ), - CircleAvatar( - backgroundColor: !logic.cameraToggle - ? ColorValues.whiteColor - : ColorValues.errorColor, - radius: Dimens.twentyFour, - child: NxIconButton( - icon: !logic.cameraToggle - ? Icons.videocam_outlined - : Icons.videocam_off_outlined, - iconColor: !logic.cameraToggle - ? Theme.of(Get.context!).iconTheme.color - : ColorValues.whiteColor, - onTap: () => logic.toggleMuteVideo(), - // onTap: () { - // logic.participants.add(AppUtils.randomIntNumeric(8)); - // logic.update(); - // }, + CircleAvatar( + backgroundColor: !logic.videoMuted + ? ColorValues.whiteColor + : ColorValues.errorColor, + radius: Dimens.thirtyTwo, + child: NxIconButton( + icon: !logic.videoMuted + ? Icons.videocam_outlined + : Icons.videocam_off_outlined, + iconColor: !logic.videoMuted + ? Theme.of(Get.context!).iconTheme.color + : ColorValues.whiteColor, + onTap: () => logic.toggleMuteVideo(), + // onTap: () { + // logic.participants.add(AppUtility.randomIntNumeric(8)); + // logic.update(); + // }, + ), ), - ), - CircleAvatar( - backgroundColor: ColorValues.errorColor, - radius: Dimens.twentyFour, - child: NxIconButton( - icon: Icons.close, - iconColor: ColorValues.whiteColor, - onTap: () => logic.leaveChannel(), + CircleAvatar( + backgroundColor: ColorValues.errorColor, + radius: Dimens.thirtyTwo, + child: NxIconButton( + icon: Icons.close, + iconColor: ColorValues.whiteColor, + onTap: () => logic.leaveChannel(), + ), ), - ), - ], + ], + ), ), ); diff --git a/lib/modules/calling/views/join_view.dart b/lib/modules/calling/views/join_view.dart index bed3e7a..fa59d96 100644 --- a/lib/modules/calling/views/join_view.dart +++ b/lib/modules/calling/views/join_view.dart @@ -21,104 +21,115 @@ class JoinView extends StatelessWidget { child: SizedBox( width: Dimens.screenWidth, height: Dimens.screenHeight, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const NxAppBar( - title: StringValues.join, - ), - GetBuilder( - builder: (con) => Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + child: GetBuilder( + builder: (logic) { + return Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Expanded(child: SizedBox()), - Padding( - padding: Dimens.edgeInsets8, + const NxAppBar( + title: StringValues.join, + ), + Expanded( child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - TextFormField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Channel ID', - hintStyle: TextStyle( - color: ColorValues.grayColor, - ), - ), - keyboardType: TextInputType.emailAddress, - maxLines: 1, - style: AppStyles.style16Normal.copyWith( - color: Theme.of(Get.context!) - .textTheme - .bodyText1! - .color, + const Expanded(child: SizedBox()), + Padding( + padding: Dimens.edgeInsets8, + child: Column( + children: [ + TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + hintText: 'Channel ID', + hintStyle: TextStyle( + color: ColorValues.grayColor, + ), + ), + keyboardType: TextInputType.emailAddress, + maxLines: 1, + style: AppStyles.style16Normal.copyWith( + color: Theme.of(Get.context!) + .textTheme + .bodyText1! + .color, + ), + controller: logic.channelIdTextController, + onEditingComplete: FocusManager + .instance.primaryFocus!.unfocus, + ), + Dimens.boxHeight8, + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Camera", + style: AppStyles.style18Normal, + ), + Switch( + onChanged: (value) { + logic.enableVideo(value); + }, + value: !logic.cameraToggle, + activeColor: ColorValues.primaryColor, + ), + ], + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Audio", + style: AppStyles.style18Normal, + ), + Switch( + onChanged: (value) { + logic.enableAudio(value); + }, + value: !logic.micToggle, + activeColor: ColorValues.primaryColor, + ), + ], + ), + ], ), - controller: con.channelIdTextController, - onEditingComplete: - FocusManager.instance.primaryFocus!.unfocus, - ), - Dimens.boxHeight8, - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Camera", - style: AppStyles.style18Normal, - ), - Switch( - onChanged: (value) { - con.enableVideo(value); - }, - value: !con.cameraToggle, - activeColor: ColorValues.primaryColor, - ), - ], - ), - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Audio", - style: AppStyles.style18Normal, - ), - Switch( - onChanged: (value) { - con.enableAudio(value); - }, - value: !con.micToggle, - activeColor: ColorValues.primaryColor, - ), - ], ), + const Expanded(child: SizedBox()), ], ), - ), - const Expanded(child: SizedBox()), - NxFilledButton( - label: StringValues.join, - borderRadius: 0.0, - onTap: () { - FocusManager.instance.primaryFocus!.unfocus(); - if (con.channelIdTextController.text.isEmpty) { - return; - } - RouteManagement.goToCallingView( - channelId: - con.channelIdTextController.text.trim(), - enableAudio: con.micToggle, - enableVideo: con.cameraToggle, - ); - }, - ), + ) ], ), - ), - ), - ], + Positioned( + bottom: Dimens.zero, + left: Dimens.zero, + right: Dimens.zero, + child: NxFilledButton( + label: StringValues.join.toUpperCase(), + borderRadius: 0.0, + onTap: () { + FocusManager.instance.primaryFocus!.unfocus(); + if (logic.channelIdTextController.text.isEmpty) { + return; + } + RouteManagement.goToCallingView( + channelId: + logic.channelIdTextController.text.trim(), + enableAudio: logic.micToggle, + enableVideo: logic.cameraToggle, + ); + }, + ), + ), + ], + ); + }, ), ), ), diff --git a/lib/modules/calling/views/start_view.dart b/lib/modules/calling/views/start_view.dart index 0c8494d..ad8bd22 100644 --- a/lib/modules/calling/views/start_view.dart +++ b/lib/modules/calling/views/start_view.dart @@ -21,90 +21,104 @@ class StartView extends StatelessWidget { child: SizedBox( width: Dimens.screenWidth, height: Dimens.screenHeight, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const NxAppBar( - title: StringValues.start, - ), - Expanded( - child: GetBuilder( - builder: (logic) => Column( - mainAxisAlignment: MainAxisAlignment.center, + child: GetBuilder( + builder: (logic) { + return Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Expanded(child: SizedBox()), - Padding( - padding: Dimens.edgeInsets8, + const NxAppBar( + title: StringValues.start, + ), + Expanded( child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Meeting ID", - style: AppStyles.style18Normal, - ), - Text( - AppUtility.formatMeetingId( - Get.find() - .channelId - .toString()), - style: AppStyles.style20Bold, - textAlign: TextAlign.center, - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Camera", - style: AppStyles.style18Normal, - ), - Switch( - onChanged: (value) { - logic.enableVideo(value); - }, - value: !logic.cameraToggle, - activeColor: ColorValues.primaryColor, - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Audio", - style: AppStyles.style18Normal, - ), - Switch( - onChanged: (value) { - logic.enableAudio(value); - }, - value: !logic.micToggle, - activeColor: ColorValues.primaryColor, - ), - ], + const Expanded(child: SizedBox()), + Padding( + padding: Dimens.edgeInsets8, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Meeting ID", + style: AppStyles.style18Normal, + ), + Text( + AppUtility.formatMeetingId( + Get.find() + .channelId + .toString()), + style: AppStyles.style20Bold, + textAlign: TextAlign.center, + ), + ], + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Camera", + style: AppStyles.style18Normal, + ), + Switch( + onChanged: (value) { + logic.enableVideo(value); + }, + value: !logic.cameraToggle, + activeColor: ColorValues.primaryColor, + ), + ], + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "Audio", + style: AppStyles.style18Normal, + ), + Switch( + onChanged: (value) { + logic.enableAudio(value); + }, + value: !logic.micToggle, + activeColor: ColorValues.primaryColor, + ), + ], + ), + ], + ), ), + const Expanded(child: SizedBox()), ], ), ), - const Expanded(child: SizedBox()), - NxFilledButton( - label: StringValues.start, - borderRadius: 0.0, - onTap: () => RouteManagement.goToCallingView( - enableAudio: logic.micToggle, - enableVideo: logic.cameraToggle, - ), - ), ], ), - ), - ), - ], + Positioned( + bottom: Dimens.zero, + left: Dimens.zero, + right: Dimens.zero, + child: NxFilledButton( + label: StringValues.start.toUpperCase(), + borderRadius: 0.0, + onTap: () => RouteManagement.goToCallingView( + enableAudio: logic.micToggle, + enableVideo: logic.cameraToggle, + ), + ), + ), + ], + ); + }, ), ), ), diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index fed3700..e274729 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -82,14 +82,16 @@ class HomeView extends StatelessWidget { ), const Expanded(child: SizedBox()), Dimens.boxHeight16, - const NxFilledButton( - label: StringValues.start, + NxFilledButton( + label: StringValues.start.toUpperCase(), onTap: RouteManagement.goToStartView, + height: Dimens.fiftySix, ), Dimens.boxHeight16, - const NxOutlinedButton( - label: StringValues.join, + NxOutlinedButton( + label: StringValues.join.toUpperCase(), onTap: RouteManagement.goToJoinView, + height: Dimens.fiftySix, ), Dimens.boxHeight16, ], diff --git a/lib/modules/profile/controllers/profile_controller.dart b/lib/modules/profile/controllers/profile_controller.dart index 6251acd..6a1bf3b 100644 --- a/lib/modules/profile/controllers/profile_controller.dart +++ b/lib/modules/profile/controllers/profile_controller.dart @@ -73,7 +73,6 @@ class ProfileController extends GetxController { _isLoading.value = false; update(); AppUtility.printLog(StringValues.connTimedOut); - AppUtility.printLog(StringValues.connTimedOut); AppUtility.showSnackBar(StringValues.connTimedOut, StringValues.error); } on FormatException catch (e) { _isLoading.value = false; diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index c084f34..22e799f 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -9,6 +9,7 @@ import 'package:video_calling_app/constants/dimens.dart'; import 'package:video_calling_app/constants/strings.dart'; import 'package:video_calling_app/constants/styles.dart'; import 'package:video_calling_app/modules/profile/controllers/profile_controller.dart'; +import 'package:video_calling_app/routes/route_management.dart'; class ProfileView extends StatelessWidget { const ProfileView({Key? key}) : super(key: key); @@ -64,12 +65,12 @@ class ProfileView extends StatelessWidget { logic.profileData.user!.avatar != null) { return NxCircleNetworkImage( imageUrl: logic.profileData.user!.avatar!.url!, - radius: Dimens.sixtyFour, + radius: Dimens.eighty, ); } return NxCircleAssetImage( imgAsset: AssetValues.avatar, - radius: Dimens.sixtyFour, + radius: Dimens.eighty, ); } @@ -89,12 +90,6 @@ class ProfileView extends StatelessWidget { color: Theme.of(Get.context!).textTheme.subtitle1!.color, ), ), - if (logic.profileData.user!.about != null) Dimens.boxHeight8, - if (logic.profileData.user!.about != null) - Text( - logic.profileData.user!.about!, - style: AppStyles.style14Normal, - ), Dimens.boxHeight16, ], ); @@ -105,9 +100,10 @@ class ProfileView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ NxFilledButton( - label: StringValues.logout, + label: StringValues.logout.toUpperCase(), onTap: () { Get.find().logout(); + RouteManagement.goToLoginView(); }, ), ], diff --git a/pubspec.lock b/pubspec.lock index 3b88e35..235de61 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: agora_rtc_engine url: "https://pub.dartlang.org" source: hosted - version: "5.1.1" + version: "6.0.0" analyzer: dependency: transitive description: @@ -316,13 +316,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.6" - equatable: - dependency: "direct main" - description: - name: equatable - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" fake_async: dependency: transitive description: @@ -471,20 +464,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" - hive: - dependency: transitive - description: - name: hive - url: "https://pub.dartlang.org" - source: hosted - version: "2.2.3" - hive_generator: - dependency: "direct dev" - description: - name: hive_generator - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.3" html: dependency: transitive description: @@ -590,6 +569,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + iris_event: + dependency: transitive + description: + name: iris_event + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e690e38..df6e001 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,9 +1,9 @@ name: video_calling_app -description: A new video calling mobile application using Flutter, Agora SDK and GetX state management. +description: A new video calling mobile application developed using Flutter, Agora SDK and GetX state management. publish_to: 'none' -version: 1.0.1+1 +version: 1.0.1+01 environment: sdk: ">=2.17.0 <3.0.0" @@ -14,7 +14,7 @@ dependencies: cupertino_icons: ^1.0.5 - agora_rtc_engine: 5.1.1 + agora_rtc_engine: ^6.0.0 wakelock: ^0.6.2 get_time_ago: ^1.1.6 @@ -45,7 +45,6 @@ dependencies: get_storage: ^2.0.3 built_value: ^8.4.1 json_annotation: ^4.7.0 - equatable: ^2.0.5 copy_with_extension: ^4.0.3 dev_dependencies: @@ -57,7 +56,6 @@ dev_dependencies: flutter_native_splash: ^2.2.10+1 build_runner: ^2.2.1 - hive_generator: ^1.1.3 json_serializable: ^6.4.1 copy_with_extension_gen: ^4.0.3