1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 12:59:21 +00:00

Simplify pages system

This commit is contained in:
Pierre HUBERT 2020-05-10 18:29:43 +02:00
parent a53ae381dc
commit a119f60fdb
6 changed files with 247 additions and 304 deletions

View File

@ -1,3 +1,23 @@
import 'package:comunic/helpers/account_helper.dart';
import 'package:comunic/ui/routes/account_settings/account_settings_route.dart';
import 'package:comunic/ui/routes/app_settings_route.dart';
import 'package:comunic/ui/routes/conversation_route.dart';
import 'package:comunic/ui/routes/login_route.dart';
import 'package:comunic/ui/routes/main_route/page_info.dart';
import 'package:comunic/ui/screens/call_screen.dart';
import 'package:comunic/ui/screens/conversations_list_screen.dart';
import 'package:comunic/ui/screens/friends_list_screen.dart';
import 'package:comunic/ui/screens/group_screen.dart';
import 'package:comunic/ui/screens/groups_list_screen.dart';
import 'package:comunic/ui/screens/newest_posts.dart';
import 'package:comunic/ui/screens/notifications_screen.dart';
import 'package:comunic/ui/screens/other_friends_lists_screen.dart';
import 'package:comunic/ui/screens/search_screen.dart';
import 'package:comunic/ui/screens/user_access_denied_screen.dart';
import 'package:comunic/ui/screens/user_page_screen.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
/// Abstract main application route
@ -16,26 +36,123 @@ mixin MainRoute implements StatefulWidget {}
/// Public interface of home controller
abstract class MainController extends State<MainRoute> {
final _pagesStack = List<PageInfo>();
/// Default page of the application
PageInfo get defaultPage;
/// Get the current page being active in the application
PageInfo get currentPage => _pagesStack.last;
/// Get the current number of page in application stack
int get numberOfPages => _pagesStack.length;
/// Get current instance of Home controller
static MainController of(BuildContext context) {
assert(context != null); // A future implementation might need context again
return mainControllerKey.currentState;
}
@override
void initState() {
super.initState();
_pagesStack.add(defaultPage);
}
/// Push a new page
void pushPage(PageInfo page) => setState(() => _pagesStack.add(page));
/// Pop current page. Do not call this method directly.
@protected
void doPopPage() {
if (_pagesStack.length > 0) setState(() => _pagesStack.removeLast());
}
/// Pop until main route is reached
void popUntilMainRoute() => Navigator.of(context).popUntil((settings) =>
ModalRoute.of(context).isCurrent || !ModalRoute.of(context).isActive);
/// Open user page
void openUserPage(int userID);
/// Open notifications page
void openNotificationsPage() => pushPage(PageInfo(
type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen()));
void openUserAccessDeniedPage(int userID);
/// Open conversations page
void openConversationsPage() => pushPage(PageInfo(
type: PageType.CONVERSATIONS_LIST_PAGE,
child: ConversationsListScreen()));
/// Open latest posts page
void openLatestPostsPage() => pushPage(
PageInfo(type: PageType.LATEST_POSTS_PAGE, child: NewestPostsScreen()));
/// Open user page
void openUserPage(int userID) => pushPage(PageInfo(
type: PageType.USER_PAGE, child: UserPageScreen(userID: userID)));
void openUserAccessDeniedPage(int userID) =>
pushPage(PageInfo(child: UserAccessDeniedScreen(userID: userID)));
/// Open current user page
void openCurrentUserPage() => this.openUserPage(userID());
/// Open a specific group page specified by its [groupID]
void openGroup(int groupID);
void openGroup(int groupID) => pushPage(PageInfo(
type: PageType.GROUP_PAGE, child: GroupPageScreen(groupID: groupID)));
/// Display the list of friends of current user
void openFriendsList() => pushPage(
PageInfo(type: PageType.FRIENDS_LIST_PAGE, child: FriendsListScreen()));
/// Open search page
void openSearchPage() => pushPage(PageInfo(child: SearchScreen()));
/// Open groups list page
void openGroupsListPage() => pushPage(PageInfo(child: GroupsListScreen()));
/// Display the list of friends of a user
void openUserFriendsList(int userID);
void openUserFriendsList(int userID) =>
pushPage(PageInfo(child: OtherUserFriendsListScreen(userID: userID)));
/// Push a new widget
void push(Widget w,
{bool hideNavBar = false, bool canShowAsDialog = false}) =>
pushPage(PageInfo(
child: w, hideNavBar: hideNavBar, canShowAsDialog: canShowAsDialog));
/// Open a conversation
void openConversation(int convID, {fullScreen: false}) => pushPage(PageInfo(
type: PageType.CONVERSATION_PAGE,
child: ConversationRoute(conversationID: convID),
hideNavBar: true,
));
/// Start a call for a given conversation
void startCall(int convID) =>
pushPage(PageInfo(child: CallScreen(convID: null), hideNavBar: true));
/// Open account settings
void openAccountSettings() => Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => AccountSettingsRoute()));
/// Open application settings
void openAppSettings() => Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => AppSettingsRoute()));
/// Handle logout requests
void requestLogout() async {
if (!await showConfirmDialog(
context: context,
message: tr("Do you really want to sign out from the application ?"),
title: tr("Sign out"))) return;
popUntilMainRoute();
await AccountHelper().signOut();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) {
return LoginRoute();
}));
}
/// Pop current page. Last page can not be popped
///
@ -46,17 +163,4 @@ abstract class MainController extends State<MainRoute> {
else
doPopPage();
}
/// Pop current page. Do not call this method directly.
@protected
void doPopPage();
/// Push a new widget
void push(Widget w, {bool hideNavBar, bool allowAsDialog});
/// Open a conversation
void openConversation(int convID, {fullScreen: false});
/// Start a call for a given conversation
void startCall(int convID);
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/cupertino.dart';
/// Single page information
///
/// @author Pierre HUBERT
enum PageType {
USER_PAGE,
GROUP_PAGE,
CONVERSATION_PAGE,
NOTIFICATIONS_PAGE,
CONVERSATIONS_LIST_PAGE,
FRIENDS_LIST_PAGE,
LATEST_POSTS_PAGE,
OTHER_PAGE
}
class PageInfo {
final PageType type;
final Widget child;
final int id;
final bool hideNavBar;
final bool canShowAsDialog;
const PageInfo({
this.type = PageType.OTHER_PAGE,
@required this.child,
this.id,
this.hideNavBar = false,
this.canShowAsDialog = false,
}) : assert(type != null),
assert(child != null),
assert(hideNavBar != null),
assert(canShowAsDialog != null);
}

View File

@ -1,27 +1,9 @@
import 'package:comunic/helpers/account_helper.dart';
import 'package:comunic/ui/routes/account_settings/account_settings_route.dart';
import 'package:comunic/ui/routes/app_settings_route.dart';
import 'package:comunic/ui/routes/conversation_route.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/screens/call_screen.dart';
import 'package:comunic/ui/screens/conversations_list_screen.dart';
import 'package:comunic/ui/screens/friends_list_screen.dart';
import 'package:comunic/ui/screens/group_screen.dart';
import 'package:comunic/ui/screens/groups_list_screen.dart';
import 'package:comunic/ui/screens/newest_posts.dart';
import 'package:comunic/ui/routes/main_route/page_info.dart';
import 'package:comunic/ui/screens/notifications_screen.dart';
import 'package:comunic/ui/screens/other_friends_lists_screen.dart';
import 'package:comunic/ui/screens/search_screen.dart';
import 'package:comunic/ui/screens/user_access_denied_screen.dart';
import 'package:comunic/ui/screens/user_page_screen.dart';
import 'package:comunic/ui/widgets/mobile_mode/mobile_appbar_widget.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
import '../login_route.dart';
/// Main route of the application
///
/// @author Pierre HUBERT
@ -33,138 +15,20 @@ class SmartphoneMainRoute extends StatefulWidget implements MainRoute {
State<StatefulWidget> createState() => _MainRouteState();
}
class CurrPage {
final BarCallbackActions action;
final Map<String, dynamic> args;
final bool hideNavBar;
const CurrPage(
this.action, {
this.args,
this.hideNavBar = false,
}) : assert(action != null),
assert(hideNavBar != null);
@override
String toString() =>
"CurrPage {\n\taction: " +
this.action.toString() +
"\n\targs: " +
this.args.toString() +
"\n}";
}
/// Private implementation of HomeController
class _MainRouteState extends MainController {
CurrPage get _currTab => history.last;
List<CurrPage> history = List();
/// Change currently visible tab
void _pushPage(CurrPage newPage) {
setState(() {
history.add(newPage);
});
}
/// Pop the page
void doPopPage() {
if (history.length > 1) history.removeLast();
setState(() {});
}
@override
PageInfo get defaultPage =>
PageInfo(type: PageType.NOTIFICATIONS_PAGE, child: NotificationsScreen());
/// Allows to go to previous tab
Future<bool> _willPop() async {
if (history.length == 1) return true;
if (numberOfPages == 1) return true;
popPage();
return false;
}
/// Handles a new tab being tapped
void _onTap(BarCallbackActions action) {
/// Check more quick actions
switch (action) {
/// Open current user page
case BarCallbackActions.OPEN_MY_PAGE:
_openCurrentUserPage();
break;
/// Open app settings page
case BarCallbackActions.OPEN_APP_SETTINGS:
_openAppSettings();
break;
case BarCallbackActions.OPEN_ACCOUNT_SETTINGS:
_openAccountsSettings();
break;
/// Logout user
case BarCallbackActions.ACTION_LOGOUT:
_logoutRequested();
break;
default:
_pushPage(CurrPage(action));
}
}
@override
void initState() {
super.initState();
// Default page: conversations list
_pushPage(CurrPage(BarCallbackActions.OPEN_NOTIFICATIONS));
}
/// Build the body of the application
Widget _buildBody(BuildContext context) {
switch (_currTab.action) {
case BarCallbackActions.OPEN_CUSTOM_WIDGET:
return _currTab.args["widget"];
case BarCallbackActions.OPEN_NOTIFICATIONS:
return NotificationsScreen();
case BarCallbackActions.OPEN_CONVERSATIONS:
return ConversationsListScreen();
case BarCallbackActions.OPEN_NEWEST_POSTS:
return NewestPostsScreen();
case BarCallbackActions.OPEN_FRIENDS:
return FriendsListScreen();
case BarCallbackActions.OPEN_USER_PAGE:
return UserPageScreen(userID: _currTab.args["userID"]);
case BarCallbackActions.OPEN_USER_ACCESS_DENIED_PAGE:
return UserAccessDeniedScreen(userID: _currTab.args["userID"]);
case BarCallbackActions.OPEN_SEARCH_PAGE:
return SearchScreen();
case BarCallbackActions.OPEN_GROUPS:
return GroupsListScreen();
case BarCallbackActions.OPEN_GROUP_PAGE:
return GroupPageScreen(groupID: _currTab.args["groupID"]);
case BarCallbackActions.OPEN_USER_FRIENDS_LIST:
return OtherUserFriendsListScreen(
userID: _currTab.args["userID"],
);
case BarCallbackActions.OPEN_CONVERSATION:
return ConversationRoute(
conversationID: _currTab.args["convID"],
);
default:
throw "Invalid tab : " + _currTab.toString();
}
}
@override
Widget build(BuildContext context) {
return Container(
@ -174,94 +38,13 @@ class _MainRouteState extends MainController {
child: WillPopScope(
onWillPop: _willPop,
child: Scaffold(
appBar: _currTab.hideNavBar
appBar: currentPage.hideNavBar
? null
: ComunicMobileAppBar(
onTap: _onTap,
selectedAction: _currTab.action,
),
body: SafeArea(
child: _buildBody(context),
),
: ComunicMobileAppBar(currentPage: currentPage),
body: SafeArea(child: currentPage.child),
),
),
),
);
}
/// Open current user page
Future<void> _openCurrentUserPage() async {
this.openUserPage(userID());
}
void _openAppSettings() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => AppSettingsRoute()));
}
void _openAccountsSettings() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (c) => AccountSettingsRoute()));
}
/// Handle logout requests from user
Future<void> _logoutRequested() async {
if (!await showConfirmDialog(
context: context,
message: tr("Do you really want to sign out from the application ?"),
title: tr("Sign out"))) return;
await AccountHelper().signOut();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (c) {
return LoginRoute();
}));
}
@override
void openGroup(int groupID) {
_pushPage(CurrPage(BarCallbackActions.OPEN_GROUP_PAGE,
args: {"groupID": groupID}));
}
@override
void openUserPage(int userID) {
_pushPage(
CurrPage(BarCallbackActions.OPEN_USER_PAGE, args: {"userID": userID}));
}
@override
void openUserAccessDeniedPage(int userID) {
_pushPage(CurrPage(BarCallbackActions.OPEN_USER_ACCESS_DENIED_PAGE,
args: {"userID": userID}));
}
@override
void openUserFriendsList(int userID) {
_pushPage(CurrPage(BarCallbackActions.OPEN_USER_FRIENDS_LIST,
args: {"userID": userID}));
}
@override
void push(Widget w, {bool hideNavBar = false, bool allowAsDialog}) {
_pushPage(CurrPage(
BarCallbackActions.OPEN_CUSTOM_WIDGET,
args: {"widget": w},
hideNavBar: hideNavBar,
));
}
@override
void openConversation(int convID, {fullScreen: false}) {
_pushPage(CurrPage(
BarCallbackActions.OPEN_CONVERSATION,
args: {"convID": convID},
hideNavBar: true,
));
}
@override
void startCall(int convID) {
push(CallScreen(convID: convID), hideNavBar: true);
}
}

View File

@ -1,5 +1,7 @@
import 'package:comunic/ui/dialogs/screen_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/routes/main_route/page_info.dart';
import 'package:comunic/ui/screens/newest_posts.dart';
import 'package:comunic/ui/widgets/tablet_mode/calls/calls_area.dart';
import 'package:comunic/ui/widgets/tablet_mode/conversations/conversations_area_widget.dart';
import 'package:comunic/ui/widgets/tablet_mode/current_user_panel.dart';
@ -23,6 +25,9 @@ class _TabletRouteState extends MainController {
final _conversationsKey = GlobalKey<ConversationsAreaWidgetState>();
final _callsKey = GlobalKey<CallsAreaState>();
@override
PageInfo get defaultPage => PageInfo(child: NewestPostsScreen());
@override
Widget build(BuildContext context) {
return Scaffold(
@ -67,47 +72,23 @@ class _TabletRouteState extends MainController {
Widget _buildRightPane() => Container();
@override
void pushPage(PageInfo page) {
if (page.canShowAsDialog == true)
showScreenDialog(context, page.child);
else
super.pushPage(page);
}
@override
void openConversation(int convID, {fullScreen = false}) {
popUntilMainRoute();
_conversationsKey.currentState.openConversations(convID);
}
@override
void openGroup(int groupID) {
// TODO: implement openGroup
}
@override
void openUserAccessDeniedPage(int userID) {
// TODO: implement openUserAccessDeniedPage
}
@override
void openUserFriendsList(int userID) {
// TODO: implement openUserFriendsList
}
@override
void openUserPage(int userID) {
// TODO: implement openUserPage
}
@override
void push(Widget w, {bool hideNavBar, bool allowAsDialog}) {
if (allowAsDialog == true) {
showScreenDialog(context, w);
return;
}
// TODO: implement push
if (!fullScreen) {
popUntilMainRoute();
_conversationsKey.currentState.openConversations(convID);
} else
super.openConversation(convID, fullScreen: fullScreen);
}
@override
void startCall(int convID) => _callsKey.currentState.openCall(convID);
@override
void doPopPage() {
// TODO: implement doPopPage
}
}

View File

@ -117,7 +117,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
/// Create a new conversation
void _createConversation() {
MainController.of(context)
.push(CreateConversationScreen(), allowAsDialog: true);
.push(CreateConversationScreen(), canShowAsDialog: true);
if (widget.onOpen != null) widget.onOpen();
}

View File

@ -1,6 +1,8 @@
import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/helpers/notifications_helper.dart';
import 'package:comunic/models/count_unread_notifications.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/routes/main_route/page_info.dart';
import 'package:comunic/ui/widgets/safe_state.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
@ -14,7 +16,6 @@ typedef OnSelectMenuAction = void Function(BarCallbackActions);
/// Callback actions
enum BarCallbackActions {
OPEN_CUSTOM_WIDGET,
OPEN_NOTIFICATIONS,
OPEN_CONVERSATIONS,
OPEN_NEWEST_POSTS,
@ -22,14 +23,8 @@ enum BarCallbackActions {
OPEN_MY_PAGE,
OPEN_SEARCH_PAGE,
OPEN_GROUPS,
OPEN_GROUP_PAGE,
OPEN_USER_PAGE,
OPEN_USER_ACCESS_DENIED_PAGE,
OPEN_ACCOUNT_SETTINGS,
OPEN_APP_SETTINGS,
OPEN_USER_FRIENDS_LIST,
OPEN_CONVERSATION,
NONE,
ACTION_LOGOUT
}
@ -43,16 +38,19 @@ class _MenuItem {
final Widget icon;
final BarCallbackActions action;
final bool isMenu;
final PageType pageType;
const _MenuItem({
@required this.label,
@required this.icon,
@required this.action,
@required this.pageType,
this.isMenu = false,
}) : assert(label != null),
assert(icon != null || isMenu),
assert(action != null),
assert(isMenu != null);
assert(isMenu != null),
assert(isMenu || icon != null),
assert(isMenu || action != null),
assert(isMenu || pageType != null);
}
/// Item of action menu
@ -70,24 +68,29 @@ final _menuItems = <_MenuItem>[
_MenuItem(
label: tr("Notifications"),
icon: Icon(Icons.notifications),
action: BarCallbackActions.OPEN_NOTIFICATIONS),
action: BarCallbackActions.OPEN_NOTIFICATIONS,
pageType: PageType.NOTIFICATIONS_PAGE),
_MenuItem(
label: tr("Conversations"),
icon: Icon(Icons.comment),
action: BarCallbackActions.OPEN_CONVERSATIONS),
action: BarCallbackActions.OPEN_CONVERSATIONS,
pageType: PageType.CONVERSATIONS_LIST_PAGE),
_MenuItem(
label: tr("Newest"),
icon: Icon(Icons.refresh),
action: BarCallbackActions.OPEN_NEWEST_POSTS),
action: BarCallbackActions.OPEN_NEWEST_POSTS,
pageType: PageType.LATEST_POSTS_PAGE),
_MenuItem(
label: tr("Friends"),
icon: Icon(Icons.group),
action: BarCallbackActions.OPEN_FRIENDS),
action: BarCallbackActions.OPEN_FRIENDS,
pageType: PageType.FRIENDS_LIST_PAGE),
_MenuItem(
label: tr("Menu"),
icon: Icon(Icons.more_vert),
isMenu: true,
action: BarCallbackActions.NONE)
action: null,
pageType: null)
];
/// List of menu actions items
@ -107,13 +110,14 @@ final _menuActionsItem = <_ActionMenuItem>[
];
/// Public widget
class ComunicMobileAppBar extends StatefulWidget implements PreferredSizeWidget {
final OnSelectMenuAction onTap;
final BarCallbackActions selectedAction;
class ComunicMobileAppBar extends StatefulWidget
implements PreferredSizeWidget {
final PageInfo currentPage;
const ComunicMobileAppBar(
{Key key, @required this.onTap, @required this.selectedAction})
: assert(onTap != null),
const ComunicMobileAppBar({
Key key,
@required this.currentPage,
}) : assert(currentPage != null),
super(key: key);
@override
@ -178,14 +182,50 @@ class _ComunicMobileAppBarState extends SafeState<ComunicMobileAppBar> {
_menuItems.length,
(i) => _MenuItemWidget(
item: _menuItems[i],
onTap: widget.onTap,
isSelected: _menuItems[i].action == widget.selectedAction,
onTap: _handleAction,
isSelected: _menuItems[i].pageType == widget.currentPage.type,
newNotice: getNumberUnread(_menuItems[i].action),
),
),
),
);
}
void _handleAction(BarCallbackActions action) {
final controller = MainController.of(context);
switch (action) {
case BarCallbackActions.OPEN_NOTIFICATIONS:
controller.openNotificationsPage();
break;
case BarCallbackActions.OPEN_CONVERSATIONS:
controller.openConversationsPage();
break;
case BarCallbackActions.OPEN_NEWEST_POSTS:
controller.openLatestPostsPage();
break;
case BarCallbackActions.OPEN_FRIENDS:
controller.openFriendsList();
break;
case BarCallbackActions.OPEN_MY_PAGE:
controller.openCurrentUserPage();
break;
case BarCallbackActions.OPEN_SEARCH_PAGE:
controller.openSearchPage();
break;
case BarCallbackActions.OPEN_GROUPS:
controller.openGroupsListPage();
break;
case BarCallbackActions.OPEN_ACCOUNT_SETTINGS:
controller.openAccountSettings();
break;
case BarCallbackActions.OPEN_APP_SETTINGS:
controller.openAppSettings();
break;
case BarCallbackActions.ACTION_LOGOUT:
controller.requestLogout();
break;
}
}
}
/// The [Widget] part of a menu item