import 'package:comunic/helpers/preferences_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/models/user.dart'; import 'package:comunic/ui/routes/push_notifications_route.dart'; import 'package:comunic/ui/routes/settings/account_image_settings.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart'; import 'package:comunic/ui/widgets/login_routes_theme.dart'; import 'package:comunic/utils/account_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:flutter/material.dart'; /// Tour route /// /// The tour is shown when the client sign into the application /// /// @author Pierre Hubert Future showTour(BuildContext context) async => await Navigator.of(context) .push(MaterialPageRoute(builder: (c) => TourRoute())); class TourRoute extends StatefulWidget { @override _TourRouteState createState() => _TourRouteState(); } class _TourRouteState extends State { final key = GlobalKey(); final _pushNotificationsKey = GlobalKey(); User currUser; int _defaultIndex = 0; Future _init() async { currUser = await UsersHelper().getSingleWithThrow(userID(), forceDownload: true); } List get _list => [ _FirstPane(), // Account image _PresentationPane( iconWidget: AccountImageWidget(user: currUser, width: 50), title: tr("Account image"), text: tr( "Account image allow to quickly recognize people.\n\nYou can decide to define one now!"), actionTitle: tr("Upload an account image"), onActionTap: (ctx) async { await uploadNewAccountImage(context); _defaultIndex = DefaultTabController.of(ctx).index; await key.currentState.refresh(); }, ), // Notifications _PresentationPane( icon: Icons.notifications, title: tr("Push notifications"), child: (c) => PushNotificationsConfigurationWidget( key: _pushNotificationsKey, onConfigured: () => setState(() {}), onChanged: () => setState(() {}), ), canGoNext: _pushNotificationsKey?.currentState?.canSubmit ?? false, onTapNext: (c) => _pushNotificationsKey.currentState.submit(), ), _PresentationPane( icon: Icons.group_add, title: tr("Friends"), text: tr( "You can search the people you know and ask them to become your friends!\n\nThis will help you to reach them to exchange information!"), ), _PresentationPane( icon: Icons.question_answer, title: tr("Conversations"), text: tr( "With Comunic, you can have conversations with all your friends.\n\nIt is also possible to make video calls!"), ), _PresentationPane( icon: Icons.group, title: tr("Groups"), text: tr( "You can join groups where people share the same interests as you!\n\nIt is also easy to create your own groups!"), ), _PresentationPane( icon: Icons.lock, title: tr("Privacy"), text: tr( "Your data is YOUR DATA. We will never use it or sell it.\n\nIf you do not trust us, you can always check out our source code to verify it!"), ), _LastPane(), ]; @override Widget build(BuildContext context) => LoginRoutesTheme( child: Scaffold( body: SafeArea( child: AsyncScreenWidget( key: key, onReload: _init, errorMessage: tr("Failed to load tour!"), onBuild: () => DefaultTabController( initialIndex: _defaultIndex, length: _list.length, child: _RouteBody( panes: _list, ), ), ), ), ), ); } class _RouteBody extends StatefulWidget { final List panes; const _RouteBody({Key key, this.panes}) : super(key: key); @override __RouteBodyState createState() => __RouteBodyState(); } class __RouteBodyState extends State<_RouteBody> { TabController _controller; bool get _isLastPane => _controller.index >= widget.panes.length - 1; _PresentationPane get _currPane => widget.panes[_controller.index] is _PresentationPane ? widget.panes[_controller.index] : null; bool get _canGoNext => _currPane?.canGoNext ?? true; @override void didUpdateWidget(_RouteBody oldWidget) { super.didUpdateWidget(oldWidget); _updateController(); } @override void didChangeDependencies() { super.didChangeDependencies(); _updateController(); } void _updateController() { _controller = DefaultTabController.of(context); _controller.addListener(() => setState(() {})); } @override Widget build(BuildContext context) => Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Spacer(flex: 1), Expanded( child: TabBarView( children: widget.panes, physics: NeverScrollableScrollPhysics(), ), flex: 10), Spacer(flex: 1), TabPageSelector(selectedColor: Colors.white), Spacer(flex: 1), OutlinedButton( onPressed: _canGoNext ? _nextOrFinish : null, child: Text(!_isLastPane ? tr("Next") : tr("Let's go!")), ), Spacer(flex: 1), ], ), ); void _nextOrFinish() async { if (_controller.indexIsChanging) return; if (!_isLastPane) { // Check if the next click has to be captured if (_currPane?.onTapNext != null) { if (!await _currPane.onTapNext(context)) return; } _controller.animateTo(_controller.index + 1); } else { (await PreferencesHelper.getInstance()) .setBool(PreferencesKeyList.IS_TOUR_SEEN, true); Navigator.of(context).pop(); } } } class _PresentationPane extends StatelessWidget { final IconData icon; final Widget iconWidget; final String title; final String text; final Function(BuildContext) child; final String actionTitle; final Function(BuildContext) onActionTap; final bool canGoNext; final Future Function(BuildContext) onTapNext; const _PresentationPane({ Key key, this.icon, this.iconWidget, @required this.title, this.text, this.child, this.actionTitle, this.onActionTap, this.canGoNext = true, this.onTapNext, }) : assert(icon != null || iconWidget != null), assert(title != null), assert(text != null || child != null), super(key: key); bool get _hasAction => actionTitle != null && onActionTap != null; @override Widget build(BuildContext context) { if (text != null) return Column( children: [ Spacer(flex: 3), icon != null ? Icon(icon, color: Colors.white, size: 50) : iconWidget, Spacer(flex: 1), Text( title, style: TextStyle(fontSize: 20), ), Spacer(flex: 1), _FixedSizeTextArea(text), Spacer(flex: 1), _hasAction ? OutlinedButton( onPressed: () => onActionTap(context), child: Text( actionTitle, style: TextStyle(color: Colors.white), ), ) : Opacity( opacity: 0, child: OutlinedButton( onPressed: null, child: Text(""), ), ), Spacer(flex: 3), ], ); return Column( children: [ Spacer(flex: 1), icon != null ? Icon(icon, color: Colors.white, size: 50) : iconWidget, Spacer(flex: 1), Text( title, style: TextStyle(fontSize: 20), ), Spacer(flex: 1), ConstrainedBox( constraints: BoxConstraints(maxHeight: 300), child: SingleChildScrollView(child: child(context))), Spacer(flex: 1), ], ); } } class _FirstPane extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Spacer(flex: 3), Text( "Comunic", style: TextStyle(fontSize: 25), ), Spacer(flex: 2), _FixedSizeTextArea(tr( "Welcome to Comunic, the social network that respect your privacy !")), Spacer(flex: 1), _FixedSizeTextArea(tr( "Let's configure a few things and present you some features of the network...")), Spacer(flex: 3), ], ); } } class _LastPane extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text( tr("The application is yours"), style: TextStyle(fontSize: 25), ), ); } } class _FixedSizeTextArea extends StatelessWidget { final String text; const _FixedSizeTextArea(this.text); @override Widget build(BuildContext context) => ConstrainedBox( constraints: BoxConstraints(maxWidth: 300), child: Text(text, textAlign: TextAlign.justify), ); }