diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 22fd773..cd5492b 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -6,5 +6,6 @@ "loginScreenForgotButtonText": "Forgot?", "loginScreenEmailTextFieldPlaceholderText": "Email", "loginScreenPasswordTextFieldPlaceholderText": "Password", - "homeScreenTodayText": "Today" + "homeScreenTodayText": "Today", + "sideMenuLogoutButtonTitle": "Logout" } diff --git a/assets/l10n/app_vi.arb b/assets/l10n/app_vi.arb index c315460..c1837f8 100644 --- a/assets/l10n/app_vi.arb +++ b/assets/l10n/app_vi.arb @@ -7,5 +7,6 @@ "loginScreenForgotButtonText": "Quên?", "loginScreenEmailTextFieldPlaceholderText": "Email", "loginScreenPasswordTextFieldPlaceholderText": "Mật khẩu", - "homeScreenTodayText": "Hôm nay" + "homeScreenTodayText": "Hôm nay", + "sideMenuLogoutButtonTitle": "Đăng xuất" } diff --git a/lib/modules/home/components/body.dart b/lib/modules/home/components/body.dart index 4985514..90308a9 100644 --- a/lib/modules/home/components/body.dart +++ b/lib/modules/home/components/body.dart @@ -6,61 +6,78 @@ class Body extends StatelessWidget { @override Widget build(BuildContext context) { final state = context.findAncestorStateOfType<_HomeViewImplState>()!; + return StreamsSelector0.value( stream: state.isProgressHUDShown, builder: (_, isShown, child) => ProgressHUD( isShow: isShown, child: child!, ), - child: Screen( - body: SimpleGestureDetector( - onVerticalSwipe: (direction) { - if (direction != SwipeDirection.down) return; - state.delegate?.didSwipeDown.add(null); - }, - child: Container( - color: Colors.black, - child: StreamsSelector0.value( - stream: state._isLoading, - builder: (_, isLoading, child) => Skeleton( - key: HomeView.skeletonKey, - isShown: isLoading, - baseColor: Colors.white.withOpacity(0.12), - highlightColor: Colors.white.withOpacity(0.5), - child: child!, - ), - child: Stack( - fit: StackFit.expand, - children: [ - StreamsSelector0>.value( - stream: state._surveys, - builder: (_, surveys, __) => surveys.isNotEmpty - ? CarouselSlider.builder( - carouselController: state._pageController, - itemCount: surveys.length, - itemBuilder: (_, index, ___) => SlideItem( - survey: surveys[index], - ), - options: CarouselOptions( - viewportFraction: 1, - initialPage: state._currentPage.value, - height: double.infinity, - onPageChanged: (index, _) => - state._currentPage.add(index), - ), - ) - : SlideItem.empty(), + child: SliderMenuContainer( + key: state._sliderMenuContainerKey, + hasAppBar: false, + drawerIcon: const SizedBox.shrink(), + slideDirection: SlideDirection.RIGHT_TO_LEFT, + sliderMenu: locator.get(), + sliderMain: StreamsSelector0.value( + stream: state._isUserInteractionEnabled, + builder: (_, isUserInteractionEnabled, child) => IgnorePointer( + ignoring: !isUserInteractionEnabled, + child: child, + ), + child: Screen( + body: SimpleGestureDetector( + onVerticalSwipe: (direction) { + if (direction != SwipeDirection.down) return; + state.delegate?.didSwipeDown.add(null); + }, + child: Container( + color: Colors.black, + child: StreamsSelector0.value( + stream: state._isLoading, + builder: (_, isLoading, child) => Skeleton( + key: HomeView.skeletonKey, + isShown: isLoading, + baseColor: Colors.white.withOpacity(0.12), + highlightColor: Colors.white.withOpacity(0.5), + child: child!, ), - const Positioned( - left: 20, - bottom: 152, - child: SafeArea(child: PageControl()), + child: Stack( + fit: StackFit.expand, + children: [ + StreamsSelector0>.value( + stream: state._surveys, + builder: (_, surveys, __) { + return surveys.isNotEmpty + ? CarouselSlider.builder( + carouselController: state._pageController, + itemCount: surveys.length, + itemBuilder: (_, index, ___) => SlideItem( + survey: surveys[index], + ), + options: CarouselOptions( + viewportFraction: 1, + initialPage: state._currentPage.value, + height: double.infinity, + onPageChanged: (index, _) => + state._currentPage.add(index), + ), + ) + : SlideItem.empty(); + }, + ), + const Positioned( + left: 20, + bottom: 152, + child: SafeArea(child: PageControl()), + ), + const Align( + alignment: Alignment.topLeft, + child: TopBar(), + ) + ], ), - const Align( - alignment: Alignment.topLeft, - child: TopBar(), - ) - ], + ), ), ), ), diff --git a/lib/modules/home/components/slide_item.dart b/lib/modules/home/components/slide_item.dart index 1f0b6d6..663144f 100644 --- a/lib/modules/home/components/slide_item.dart +++ b/lib/modules/home/components/slide_item.dart @@ -7,7 +7,7 @@ class SlideItem extends StatelessWidget { const SlideItem({ Key? key, required this.survey, - }) : _isEmptied = false, + }) : _isEmptied = false, super(key: key); const SlideItem._({ diff --git a/lib/modules/home/components/top_bar.dart b/lib/modules/home/components/top_bar.dart index a85faf1..48d080a 100644 --- a/lib/modules/home/components/top_bar.dart +++ b/lib/modules/home/components/top_bar.dart @@ -51,6 +51,7 @@ class TopBar extends StatelessWidget { ], ), PlatformButton( + onPressed: () => state.delegate?.userAvatarDidTap.add(null), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(18), diff --git a/lib/modules/home/home_module.dart b/lib/modules/home/home_module.dart index e2e37eb..1f66d19 100644 --- a/lib/modules/home/home_module.dart +++ b/lib/modules/home/home_module.dart @@ -2,6 +2,7 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart' hide Router; import 'package:flutter/widgets.dart' hide Router; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:flutter_slider_drawer/flutter_slider_drawer.dart'; import 'package:intl/intl.dart'; import 'package:simple_gesture_detector/simple_gesture_detector.dart'; import 'package:skeletor/skeletor.dart'; @@ -14,6 +15,7 @@ import 'package:survey/models/survey_info.dart'; import 'package:survey/models/user_info.dart'; import 'package:survey/modules/screen.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:survey/modules/side_menu/side_menu_module.dart'; import 'package:survey/modules/survey_detail/survey_detail_module.dart'; import 'package:survey/repositories/auth_repository.dart'; import 'package:survey/repositories/survey_repository.dart'; diff --git a/lib/modules/home/home_presenter.dart b/lib/modules/home/home_presenter.dart index 711f4fb..ac0cabf 100644 --- a/lib/modules/home/home_presenter.dart +++ b/lib/modules/home/home_presenter.dart @@ -9,6 +9,7 @@ class HomePresenterImpl extends HomePresenter stateDidInit.voidListen(_stateDidInit).addTo(disposeBag); showDetailButtonDidTap.listen(_showDetailButtonDidTap).addTo(disposeBag); didSwipeDown.voidListen(_didSwipeDown).addTo(disposeBag); + userAvatarDidTap.voidListen(_userAvatarDidTap).addTo(disposeBag); surveysDidFetch.listen(_surveysDidFetch).addTo(disposeBag); surveysDidFailToFetch.listen(_surveysDidFailToFetch).addTo(disposeBag); @@ -28,6 +29,15 @@ class HomePresenterImpl extends HomePresenter @override final alertDialogDidClose = BehaviorSubject(); + @override + final userAvatarDidTap = BehaviorSubject(); + + @override + final sideMenuDidDismiss = BehaviorSubject(); + + @override + final sideMenuDidShow = BehaviorSubject(); + @override final surveysDidFailToFetch = BehaviorSubject(); @@ -77,4 +87,8 @@ class HomePresenterImpl extends HomePresenter view.showProgressHUD(); interactor.fetchSurveys(force: true); } + + void _userAvatarDidTap() { + view.showSideMenu(); + } } diff --git a/lib/modules/home/home_view.dart b/lib/modules/home/home_view.dart index 407adc3..e5288d4 100644 --- a/lib/modules/home/home_view.dart +++ b/lib/modules/home/home_view.dart @@ -6,6 +6,12 @@ abstract class HomeViewDelegate implements AlertViewMixinDelegate { BehaviorSubject get showDetailButtonDidTap; BehaviorSubject get didSwipeDown; + + BehaviorSubject get userAvatarDidTap; + + BehaviorSubject get sideMenuDidShow; + + BehaviorSubject get sideMenuDidDismiss; } abstract class HomeView extends View @@ -18,6 +24,8 @@ abstract class HomeView extends View static const dotPageControlKey = Key("dot_page_control_key"); static const skeletonKey = Key("skeleton_key"); static const showDetailButtonKey = Key("show_detail_button"); + static const sliderMenuContainerKey = Key("slider_menu_container"); + static const dotPageControlHighlightColor = Colors.white; static const dotPageControlNormalColor = Color.fromRGBO(255, 255, 255, 0.2); @@ -32,6 +40,10 @@ abstract class HomeView extends View void beginSkeletonAnimation(); void stopSkeletonAnimation(); + + void showSideMenu(); + + void setUserInteractionEnable({required bool isEnabled}); } class HomeViewImpl extends StatefulWidget { @@ -51,11 +63,30 @@ class _HomeViewImplState final _pageController = CarouselController(); final _currentPage = BehaviorSubject.seeded(0); final _isLoading = BehaviorSubject.seeded(false); + final _isUserInteractionEnabled = BehaviorSubject.seeded(true); + final _sliderMenuContainerKey = GlobalKey(); @override void initState() { super.initState(); delegate?.stateDidInit.add(null); + + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + final animationController = + _sliderMenuContainerKey.currentState?.animationController; + animationController?.addListener(() { + switch (animationController.status) { + case AnimationStatus.completed: + delegate?.sideMenuDidShow.add(null); + break; + case AnimationStatus.dismissed: + delegate?.sideMenuDidDismiss.add(null); + break; + default: + break; + } + }); + }); } @override @@ -89,4 +120,14 @@ class _HomeViewImplState void beginSkeletonAnimation() { _isLoading.add(true); } + + @override + void showSideMenu() { + _sliderMenuContainerKey.currentState?.openDrawer(); + } + + @override + void setUserInteractionEnable({required bool isEnabled}) { + _isUserInteractionEnabled.add(isEnabled); + } } diff --git a/lib/modules/side_menu/components/actions.dart b/lib/modules/side_menu/components/actions.dart new file mode 100644 index 0000000..16aabe9 --- /dev/null +++ b/lib/modules/side_menu/components/actions.dart @@ -0,0 +1,24 @@ +part of '../side_menu_module.dart'; + +class Actions extends StatelessWidget { + const Actions({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final state = context.findAncestorStateOfType<_SideMenuViewImplState>()!; + + return PlatformButton( + onPressed: () => state.delegate?.logoutButtonDidTap.add(null), + cupertino: (_, __) => CupertinoButtonData( + padding: EdgeInsets.zero, + ), + child: Text( + AppLocalizations.of(context)!.sideMenuLogoutButtonTitle, + style: TextStyle( + fontSize: 20, + color: Colors.white.withOpacity(0.5), + ), + ), + ); + } +} diff --git a/lib/modules/side_menu/components/content.dart b/lib/modules/side_menu/components/content.dart new file mode 100644 index 0000000..e2488b3 --- /dev/null +++ b/lib/modules/side_menu/components/content.dart @@ -0,0 +1,37 @@ +part of '../side_menu_module.dart'; + +class Content extends StatelessWidget { + const Content({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Screen( + body: Container( + color: const Color(0xFF1E1E1E).withOpacity(0.9), + padding: const EdgeInsets.symmetric(horizontal: 20), + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 40, + ), + const User(), + const SizedBox( + height: 20, + ), + Container( + color: Colors.white.withOpacity(0.2), + height: 1, + ), + const SizedBox( + height: 35, + ), + const Actions(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/side_menu/components/user.dart b/lib/modules/side_menu/components/user.dart new file mode 100644 index 0000000..c876bc8 --- /dev/null +++ b/lib/modules/side_menu/components/user.dart @@ -0,0 +1,48 @@ +part of '../side_menu_module.dart'; + +class User extends StatelessWidget { + const User({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final state = context.findAncestorStateOfType<_SideMenuViewImplState>()!; + + return StreamsSelector0.value( + stream: state._user, + builder: (_, user, __) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + user.email!, + maxLines: 10, + style: const TextStyle( + color: Colors.white, + fontSize: 34, + fontWeight: FontWeight.w800, + ), + ), + ), + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 8), + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + color: Colors.grey, + ), + width: 36, + height: 36, + child: ClipRRect( + borderRadius: BorderRadius.circular(18), + child: Image( + image: NetworkImage(user.avatarUrl!), + ), + ), + ) + ], + ); + }); + } +} diff --git a/lib/modules/side_menu/side_menu_interactor.dart b/lib/modules/side_menu/side_menu_interactor.dart new file mode 100644 index 0000000..6efda96 --- /dev/null +++ b/lib/modules/side_menu/side_menu_interactor.dart @@ -0,0 +1,10 @@ +part of 'side_menu_module.dart'; + +abstract class SideMenuInteractorDelegate {} + +abstract class SideMenuInteractor + extends Interactor {} + +class SideMenuInteractorImpl extends SideMenuInteractor { + // +} diff --git a/lib/modules/side_menu/side_menu_module.dart b/lib/modules/side_menu/side_menu_module.dart index 3f5255b..d123fa6 100644 --- a/lib/modules/side_menu/side_menu_module.dart +++ b/lib/modules/side_menu/side_menu_module.dart @@ -1,16 +1,31 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart' hide Router; +import 'package:flutter/widgets.dart' hide Router; +import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:streams_provider/streams_provider.dart'; import 'package:survey/core/viper/module.dart'; +import 'package:survey/models/user_info.dart'; import 'package:survey/modules/screen.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +part 'side_menu_interactor.dart'; -class SideMenuModule extends Module { +part 'side_menu_router.dart'; + +part 'side_menu_presenter.dart'; + +part 'side_menu_view.dart'; + +part 'components/content.dart'; + +part 'components/user.dart'; + +part 'components/actions.dart'; + +class SideMenuModule extends Module { static const routePath = "/side-menu"; @override Widget build(BuildContext context) { - return const Screen( - body: Center( - child: Text("Side Menu"), - ), - ); + return const SideMenuViewImpl(); } } diff --git a/lib/modules/side_menu/side_menu_presenter.dart b/lib/modules/side_menu/side_menu_presenter.dart new file mode 100644 index 0000000..95ee1f8 --- /dev/null +++ b/lib/modules/side_menu/side_menu_presenter.dart @@ -0,0 +1,10 @@ +part of 'side_menu_module.dart'; + +abstract class SideMenuPresenter + extends Presenter {} + +class SideMenuPresenterImpl extends SideMenuPresenter + implements SideMenuViewDelegate, SideMenuInteractorDelegate { + @override + final logoutButtonDidTap = BehaviorSubject(); +} diff --git a/lib/modules/side_menu/side_menu_router.dart b/lib/modules/side_menu/side_menu_router.dart new file mode 100644 index 0000000..b5fadbd --- /dev/null +++ b/lib/modules/side_menu/side_menu_router.dart @@ -0,0 +1,7 @@ +part of 'side_menu_module.dart'; + +abstract class SideMenuRouter extends Router {} + +class SideMenuRouterImpl extends SideMenuRouter { + // +} diff --git a/lib/modules/side_menu/side_menu_view.dart b/lib/modules/side_menu/side_menu_view.dart new file mode 100644 index 0000000..86a6d13 --- /dev/null +++ b/lib/modules/side_menu/side_menu_view.dart @@ -0,0 +1,43 @@ +part of 'side_menu_module.dart'; + +abstract class SideMenuViewDelegate { + BehaviorSubject get logoutButtonDidTap; +} + +abstract class SideMenuView extends View { + void setUser(UserInfo user); +} + +class SideMenuViewImpl extends StatefulWidget { + const SideMenuViewImpl({Key? key}) : super(key: key); + + @override + _SideMenuViewImplState createState() => _SideMenuViewImplState(); +} + +class _SideMenuViewImplState + extends ViewState + implements SideMenuView { + final _user = BehaviorSubject(); + + @override + void initState() { + super.initState(); + + final user = UserInfo(); + user.email = "mark@nimblehq.co"; + user.avatarUrl = + "https://en.gravatar.com/userimage/137591909/6a42a5a20cd79d50edb957644bc41b0c.png"; + setUser(user); + } + + @override + Widget build(BuildContext context) { + return const Content(); + } + + @override + void setUser(UserInfo user) { + _user.add(user); + } +} diff --git a/lib/services/locator/locator_service.dart b/lib/services/locator/locator_service.dart index a98b938..fcc993a 100644 --- a/lib/services/locator/locator_service.dart +++ b/lib/services/locator/locator_service.dart @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart'; import 'package:survey/modules/home/home_module.dart'; import 'package:survey/modules/landing/landing_module.dart'; import 'package:survey/modules/login/login_module.dart'; +import 'package:survey/modules/side_menu/side_menu_module.dart'; import 'package:survey/repositories/survey_repository.dart'; import 'package:survey/services/api/api_service.dart'; import 'package:survey/services/api/auth/auth_api_service.dart'; diff --git a/lib/services/locator/locator_service_register.dart b/lib/services/locator/locator_service_register.dart index 67dc620..406e584 100644 --- a/lib/services/locator/locator_service_register.dart +++ b/lib/services/locator/locator_service_register.dart @@ -32,5 +32,11 @@ class LocatorServiceRegister { locator.registerFactory(() => HomeInteractorImpl()); locator.registerFactory(() => HomeRouterImpl()); locator.registerFactory(() => HomePresenterImpl()); + + // Side Menu + locator.registerFactory(() => SideMenuModule()); + locator.registerFactory(() => SideMenuInteractorImpl()); + locator.registerFactory(() => SideMenuRouterImpl()); + locator.registerFactory(() => SideMenuPresenterImpl()); } } diff --git a/pubspec.lock b/pubspec.lock index f6604fc..54c9e84 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -298,6 +298,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.6.1" + flutter_slider_drawer: + dependency: "direct main" + description: + name: flutter_slider_drawer + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" flutter_svg: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c488ec4..c54cb25 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: carousel_slider: ^4.0.0-nullsafety.0 skeletor: ^0.0.2 simple_gesture_detector: ^0.2.0 + flutter_slider_drawer: ^2.0.0 dev_dependencies: flutter_test: diff --git a/test/modules/home/home_presenter_test.mocks.dart b/test/modules/home/home_presenter_test.mocks.dart index da6d37f..4ac6116 100644 --- a/test/modules/home/home_presenter_test.mocks.dart +++ b/test/modules/home/home_presenter_test.mocks.dart @@ -62,6 +62,14 @@ class MockHomeView extends _i1.Mock implements _i4.HomeView { super.noSuchMethod(Invocation.method(#stopSkeletonAnimation, []), returnValueForMissingStub: null); @override + void showSideMenu() => + super.noSuchMethod(Invocation.method(#showSideMenu, []), + returnValueForMissingStub: null); + @override + void setUserInteractionEnable({bool? isEnabled}) => super.noSuchMethod( + Invocation.method(#setUserInteractionEnable, [], {#isEnabled: isEnabled}), + returnValueForMissingStub: null); + @override void alert(Object? error) => super.noSuchMethod(Invocation.method(#alert, [error]), returnValueForMissingStub: null); diff --git a/test/modules/home/home_router_test.dart b/test/modules/home/home_router_test.dart index 153d5e7..06fe271 100644 --- a/test/modules/home/home_router_test.dart +++ b/test/modules/home/home_router_test.dart @@ -1,6 +1,5 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:quick_test/quick_test.dart'; import 'package:survey/models/survey_info.dart'; diff --git a/test/modules/home/home_view_test.dart b/test/modules/home/home_view_test.dart index 58e8c5a..ab432f3 100644 --- a/test/modules/home/home_view_test.dart +++ b/test/modules/home/home_view_test.dart @@ -11,11 +11,28 @@ import 'package:survey/models/user_info.dart'; import 'package:survey/modules/home/home_module.dart'; import 'package:survey/core/viper/module.dart'; import 'package:mockito/mockito.dart'; +import 'package:survey/modules/side_menu/side_menu_module.dart'; +import 'package:survey/services/locator/locator_service.dart'; import '../../fakers/fake_module.dart'; import '../../helpers/behavior_subject_generator.dart'; import '../../helpers/extensions/widget_tester.dart'; import 'home_view_test.mocks.dart'; +class FakeSideMenuModule extends SideMenuModule { + @override + SideMenuInteractor get interactor => throw UnimplementedError(); + + @override + SideMenuPresenter get presenter => throw UnimplementedError(); + @override + SideMenuRouter get router => throw UnimplementedError(); + + @override + Widget build(BuildContext context) { + return Container(); + } +} + @GenerateMocks([HomeViewDelegate]) void main() { describe("a Home view", () { @@ -36,6 +53,7 @@ void main() { beforeEach((tester) async { HttpOverrides.global = null; + locator.registerSingleton(FakeSideMenuModule()); generator = BehaviorSubjectGenerator(); delegate = MockHomeViewDelegate(); @@ -202,6 +220,7 @@ void main() { module.view.beginSkeletonAnimation(); await tester.pump(); await tester.pump(); + await tester.pump(); }); it("triggers skeleton begin animation", (tester) async { @@ -216,6 +235,7 @@ void main() { module.view.stopSkeletonAnimation(); await tester.pump(); await tester.pump(); + await tester.pump(); }); it("triggers skeleton stop animation", (tester) async { diff --git a/test/modules/home/home_view_test.mocks.dart b/test/modules/home/home_view_test.mocks.dart index 498ddb0..df5f49f 100644 --- a/test/modules/home/home_view_test.mocks.dart +++ b/test/modules/home/home_view_test.mocks.dart @@ -39,6 +39,18 @@ class MockHomeViewDelegate extends _i1.Mock implements _i3.HomeViewDelegate { Invocation.getter(#didSwipeDown), returnValue: _FakeBehaviorSubject()) as _i2.BehaviorSubject); @override + _i2.BehaviorSubject get userAvatarDidTap => (super.noSuchMethod( + Invocation.getter(#userAvatarDidTap), + returnValue: _FakeBehaviorSubject()) as _i2.BehaviorSubject); + @override + _i2.BehaviorSubject get sideMenuDidShow => (super.noSuchMethod( + Invocation.getter(#sideMenuDidShow), + returnValue: _FakeBehaviorSubject()) as _i2.BehaviorSubject); + @override + _i2.BehaviorSubject get sideMenuDidDismiss => (super.noSuchMethod( + Invocation.getter(#sideMenuDidDismiss), + returnValue: _FakeBehaviorSubject()) as _i2.BehaviorSubject); + @override _i2.BehaviorSubject get alertDialogDidClose => (super.noSuchMethod( Invocation.getter(#alertDialogDidClose), returnValue: _FakeBehaviorSubject()) as _i2.BehaviorSubject);