diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8f2981f..b4e6d8f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -304,7 +304,7 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( diff --git a/lib/src/screens/home_screen.dart b/lib/src/screens/home_screen.dart index e8f9b6c..b729d3f 100644 --- a/lib/src/screens/home_screen.dart +++ b/lib/src/screens/home_screen.dart @@ -7,6 +7,7 @@ import '../widgets/home_app_bar.dart'; import '../widgets/new_item_dialog_route.dart'; import '../widgets/task_list_tile.dart'; import '../widgets/loading_indicator.dart'; +import '../widgets/populated_drawer.dart'; import '../widgets/search-box.dart'; class HomeScreen extends StatefulWidget { @@ -29,14 +30,21 @@ class _HomeScreenState extends State { return StreamBuilder( stream: bloc.userStream, builder: (BuildContext context, AsyncSnapshot userSnap) { - String userAvatarUrl, userDisplayName; + String userAvatarUrl = '', userDisplayName = '', userEmail; if (userSnap.hasData) { userAvatarUrl = userSnap.data.photoUrl; userDisplayName = userSnap.data.displayName; + userEmail = userSnap.data.email; } return Scaffold( + drawer: PopulatedDrawer( + userAvatarUrl: userAvatarUrl, + userDisplayName: userDisplayName, + userEmail: userEmail, + selectedScreen: Screen.home, + ), floatingActionButton: FloatingActionButton( child: Icon(FontAwesomeIcons.plus), backgroundColor: Color(0xFF707070), diff --git a/lib/src/widgets/app_bar.dart b/lib/src/widgets/app_bar.dart index 49ef387..7f98b3a 100644 --- a/lib/src/widgets/app_bar.dart +++ b/lib/src/widgets/app_bar.dart @@ -17,9 +17,12 @@ class AppBar extends StatelessWidget implements PreferredSizeWidget { /// It will vary depending on the existance of the bottom widget. final double _appBarHeight; + /// Whether to show a back button or a menu button. + final bool hasDrawer; AppBar({ this.title = '', this.bottom, + this.hasDrawer = false, }) : _appBarHeight = bottom == null ? 140.0 : 120.0; /// The preferred size of the app bar. diff --git a/lib/src/widgets/avatar.dart b/lib/src/widgets/avatar.dart new file mode 100644 index 0000000..dc4d8c9 --- /dev/null +++ b/lib/src/widgets/avatar.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +class Avatar extends StatelessWidget { + /// The url of hte image to be displayed. + final String imageUrl; + + /// The size of the Avatar. + final double size; + + Avatar({ + this.imageUrl, + this.size = 60.0, + }); + + Widget build(BuildContext context) { + return imageUrl == null + ? Container( + height: size, + width: size, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(30), + ), + child: Center( + child: Icon( + FontAwesomeIcons.question, + ), + ), + ) + : ClipOval( + child: Image.network( + imageUrl, + height: size, + width: size, + ), + ); + } +} diff --git a/lib/src/widgets/home_app_bar.dart b/lib/src/widgets/home_app_bar.dart index 590b537..6e8775d 100644 --- a/lib/src/widgets/home_app_bar.dart +++ b/lib/src/widgets/home_app_bar.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import './logo.dart'; +import './avatar.dart'; //TODO: Add callback for the menu button. @@ -32,7 +33,7 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildTopSection(), + buildTopSection(context), SizedBox( height: 20, ), @@ -58,19 +59,26 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget { ); } - Widget buildTopSection() { + Widget buildTopSection(BuildContext context) { + final scaffolContext = Scaffold.of(context); + return Row( children: [ SizedBox( width: 20, ), - Icon( - FontAwesomeIcons.bars, + IconButton( + onPressed: () => scaffolContext.openDrawer(), + icon: Icon( + FontAwesomeIcons.bars, + size: 24, + ), color: Colors.white, - size: 24, ), Spacer(), - maybeBuildAvatar(), + Avatar( + imageUrl: avatarUrl, + ), SizedBox( width: 20, ) @@ -78,30 +86,6 @@ class HomeAppBar extends StatelessWidget implements PreferredSizeWidget { ); } - Widget maybeBuildAvatar() { - return avatarUrl == null - ? Container( - height: 60, - width: 60, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(30), - ), - child: Center( - child: Icon( - FontAwesomeIcons.question, - ), - ), - ) - : ClipOval( - child: Image.network( - avatarUrl, - height: 60, - width: 60, - ), - ); - } - @override final preferredSize = Size.fromHeight(220.0); } diff --git a/lib/src/widgets/populated_drawer.dart b/lib/src/widgets/populated_drawer.dart new file mode 100644 index 0000000..115a853 --- /dev/null +++ b/lib/src/widgets/populated_drawer.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; + +import '../services/auth_service.dart'; +import '../widgets/avatar.dart'; +import '../widgets/gradient_touchable_container.dart'; + +class PopulatedDrawer extends StatelessWidget { + /// The user's display name. + final String userDisplayName; + + /// The url for the user's avatar. + final String userAvatarUrl; + + /// The current user's email. + final String userEmail; + + /// The selected screen. + final Screen selectedScreen; + + PopulatedDrawer({ + this.userDisplayName = '', + this.userAvatarUrl, + this.userEmail = '', + @required this.selectedScreen, + }) : assert(selectedScreen != null), + assert(userDisplayName != null), + assert(userEmail != null); + + Widget build(BuildContext context) { + return Drawer( + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Material( + elevation: 10, + child: UserAccountsDrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).canvasColor, + ), + accountEmail: Text(userEmail), + accountName: Text(userDisplayName), + currentAccountPicture: Avatar( + imageUrl: userAvatarUrl, + ), + ), + ), + SizedBox( + height: 10, + ), + buildDrawerTile( + text: 'Home', + isSelected: selectedScreen == Screen.home, + action: () => Navigator.of(context) + .pushNamedAndRemoveUntil('home/', (_) => false), + ), + buildDrawerTile( + text: 'Events', + isSelected: selectedScreen == Screen.events, + action: () => Navigator.of(context) + .pushNamedAndRemoveUntil('events/', (_) => false), + ), + Spacer(), + Padding( + padding: const EdgeInsets.only(left: 10), + child: GradientTouchableContainer( + onTap: () => onLogoutTap(context), + child: Text( + 'LOGOUT', + style: TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget buildDrawerTile({ + String text, + bool isSelected = false, + VoidCallback action, + }) { + final result = Container( + color: isSelected ? Colors.white10 : null, + height: 50, + padding: EdgeInsets.only( + left: 10, + ), + child: Row( + children: [ + Text( + text, + style: TextStyle( + color: Colors.white, + fontSize: 16, + ), + ), + ], + ), + ); + if (isSelected) { + return result; + } + return GestureDetector( + onTap: action, + child: result, + ); + } + + void onLogoutTap(BuildContext context) { + authService.signOut(); + // Push the login screen and remove all other screens from the navigator. + Navigator.of(context).pushNamedAndRemoveUntil('login/', (_) => false); + } +} + +enum Screen { + home, + events, +}