Skip to content

Commit

Permalink
feat: create ui for side menu screen (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
markgravity committed Jun 2, 2021
1 parent 7120e4e commit 390a652
Show file tree
Hide file tree
Showing 22 changed files with 385 additions and 56 deletions.
3 changes: 2 additions & 1 deletion assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"forgotPasswordScreenDescription": "Enter your email to receive instructions for resetting your password.",
"forgotPasswordScreenEmailTextFieldPlaceholder": "Email",
"forgotPasswordScreenResetButtonTitle": "Reset",
"homeScreenTodayText": "Today"
"homeScreenTodayText": "Today",
"sideMenuLogoutButtonTitle": "Logout"
}
3 changes: 2 additions & 1 deletion assets/l10n/app_vi.arb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"forgotPasswordScreenDescription": "Nhập tài khoản email của bạn để nhận huớng dẫn khôi phục mật khẩu.",
"forgotPasswordScreenEmailTextFieldPlaceholder": "Email",
"forgotPasswordScreenResetButtonTitle": "Khôi phục",
"homeScreenTodayText": "Hôm nay"
"homeScreenTodayText": "Hôm nay",
"sideMenuLogoutButtonTitle": "Đăng xuất"
}
111 changes: 64 additions & 47 deletions lib/modules/home/components/body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,78 @@ class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
final state = context.findAncestorStateOfType<_HomeViewImplState>()!;

return StreamsSelector0<bool>.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<bool>.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<List<SurveyInfo>>.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<SideMenuModule>(),
sliderMain: StreamsSelector0<bool>.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<bool>.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<List<SurveyInfo>>.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(),
)
],
),
),
),
),
Expand Down
1 change: 1 addition & 0 deletions lib/modules/home/components/top_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class TopBar extends StatelessWidget {
],
),
PlatformButton(
onPressed: () => state.delegate?.userAvatarDidTap.add(null),
materialFlat: (_, __) => MaterialFlatButtonData(
color: Colors.transparent,
),
Expand Down
2 changes: 2 additions & 0 deletions lib/modules/home/home_module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down
14 changes: 14 additions & 0 deletions lib/modules/home/home_presenter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -28,6 +29,15 @@ class HomePresenterImpl extends HomePresenter
@override
final alertDialogDidClose = BehaviorSubject<void>();

@override
final userAvatarDidTap = BehaviorSubject<void>();

@override
final sideMenuDidDismiss = BehaviorSubject<void>();

@override
final sideMenuDidShow = BehaviorSubject<void>();

@override
final surveysDidFailToFetch = BehaviorSubject<Exception>();

Expand Down Expand Up @@ -77,4 +87,8 @@ class HomePresenterImpl extends HomePresenter
view.showProgressHUD();
interactor.fetchSurveys(force: true);
}

void _userAvatarDidTap() {
view.showSideMenu();
}
}
41 changes: 41 additions & 0 deletions lib/modules/home/home_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ abstract class HomeViewDelegate implements AlertViewMixinDelegate {
BehaviorSubject<SurveyInfo> get showDetailButtonDidTap;

BehaviorSubject<void> get didSwipeDown;

BehaviorSubject<void> get userAvatarDidTap;

BehaviorSubject<void> get sideMenuDidShow;

BehaviorSubject<void> get sideMenuDidDismiss;
}

abstract class HomeView extends View<HomeViewDelegate>
Expand All @@ -18,6 +24,8 @@ abstract class HomeView extends View<HomeViewDelegate>
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);

Expand All @@ -32,6 +40,10 @@ abstract class HomeView extends View<HomeViewDelegate>
void beginSkeletonAnimation();

void stopSkeletonAnimation();

void showSideMenu();

void setUserInteractionEnable({required bool isEnabled});
}

class HomeViewImpl extends StatefulWidget {
Expand All @@ -51,11 +63,30 @@ class _HomeViewImplState
final _pageController = CarouselController();
final _currentPage = BehaviorSubject<int>.seeded(0);
final _isLoading = BehaviorSubject<bool>.seeded(false);
final _isUserInteractionEnabled = BehaviorSubject<bool>.seeded(true);
final _sliderMenuContainerKey = GlobalKey<SliderMenuContainerState>();

@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
Expand Down Expand Up @@ -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);
}
}
27 changes: 27 additions & 0 deletions lib/modules/side_menu/components/actions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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,
),
materialFlat: (_, __) => MaterialFlatButtonData(
color: Colors.transparent,
),
child: Text(
AppLocalizations.of(context)!.sideMenuLogoutButtonTitle,
style: TextStyle(
fontSize: 20,
color: Colors.white.withOpacity(0.5),
),
),
);
}
}
37 changes: 37 additions & 0 deletions lib/modules/side_menu/components/content.dart
Original file line number Diff line number Diff line change
@@ -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(),
],
),
),
),
);
}
}
48 changes: 48 additions & 0 deletions lib/modules/side_menu/components/user.dart
Original file line number Diff line number Diff line change
@@ -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<UserInfo>.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!),
),
),
)
],
);
});
}
}
Loading

0 comments on commit 390a652

Please sign in to comment.