diff --git a/app/assets/images/welcome-page-background.png b/app/assets/images/welcome-page-background.png new file mode 100644 index 000000000..0746f4f55 Binary files /dev/null and b/app/assets/images/welcome-page-background.png differ diff --git a/app/lib/onboarding/mobile_welcome_page.dart b/app/lib/onboarding/mobile_welcome_page.dart new file mode 100644 index 000000000..d523421be --- /dev/null +++ b/app/lib/onboarding/mobile_welcome_page.dart @@ -0,0 +1,278 @@ +// Copyright (c) 2022 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:sharezone/auth/login_page.dart'; +import 'package:sharezone/onboarding/sign_up/sign_up_page.dart'; +import 'package:sharezone_widgets/sharezone_widgets.dart'; + +class MobileWelcomePage extends StatelessWidget { + const MobileWelcomePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFE7EBED), + body: Stack( + children: const [ + _BackgroundImage(), + _Bottom(), + ], + ), + ); + } +} + +class _BackgroundImage extends StatelessWidget { + const _BackgroundImage(); + + @override + Widget build(BuildContext context) { + final isTablet = MediaQuery.of(context).size.shortestSide >= 600; + final isPortrait = + MediaQuery.of(context).orientation == Orientation.portrait; + return Positioned.fill( + child: Stack( + children: [ + Align( + alignment: Alignment.topCenter, + child: Image.asset( + 'assets/images/welcome-page-background.png', + fit: BoxFit.cover, + height: + MediaQuery.of(context).size.height * (isTablet ? 0.9 : 0.8), + semanticLabel: + 'Hintergrundbild der Willkommens-Seite mit 5 Handys, die die Sharezone-App zeigen.', + ), + ), + Positioned.fill( + child: Container( + // Add white (bottom) to transparent (top) gradient that is placed at + // the bottom of the screen. It should have a height of 30% of the + // screen height. + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.white, + // We shouldn't use `Colors.transparent` here, because it + // will destroy the gradient effect. + Colors.white.withOpacity(0.0), + ], + stops: [ + if (isTablet) isPortrait ? 0.15 : 0.22 else 0.3, + 0.5, + ], + ), + ), + ), + ), + ], + ), + ); + } +} + +class _Bottom extends StatelessWidget { + const _Bottom(); + + @override + Widget build(BuildContext context) { + return Positioned.fill( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(12), + child: DefaultTextStyle.merge( + textAlign: TextAlign.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: AnimationConfiguration.toStaggeredList( + duration: const Duration(milliseconds: 1250), + childAnimationBuilder: (widget) => SlideAnimation( + verticalOffset: 20, + child: FadeInAnimation(child: widget), + ), + children: const [ + _Headline(), + _SubHeadline(), + _NewAtSharezoneButton(), + _AlreadyHaveAnAccountButton(), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _Headline extends StatelessWidget { + const _Headline(); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(bottom: 4), + child: Text( + 'Gemeinsam den\nSchulalltag organisieren 🚀', + style: TextStyle( + fontSize: 22, + height: 1.1, + color: Colors.black, + ), + ), + ); + } +} + +class _SubHeadline extends StatelessWidget { + const _SubHeadline(); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(bottom: 20), + child: Text( + 'Optional kannst du Sharezone auch komplett alleine verwenden.', + style: TextStyle( + color: Colors.grey, + fontSize: 12, + ), + ), + ); + } +} + +class _NewAtSharezoneButton extends StatelessWidget { + const _NewAtSharezoneButton(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _BaseButton( + text: const Text( + 'Ich bin neu bei Sharezone 👋', + textAlign: TextAlign.center, + ), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SignUpPage( + withLogin: false, + withBackButton: true, + ), + settings: const RouteSettings(name: SignUpPage.tag), + ), + ), + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + ), + ); + } +} + +class _AlreadyHaveAnAccountButton extends StatelessWidget { + const _AlreadyHaveAnAccountButton(); + + @override + Widget build(BuildContext context) { + return _BaseButton( + key: const Key('go-to-login-button-E2E'), + text: Column( + children: const [ + Text( + 'Anmelden', + style: TextStyle( + fontSize: 18, + color: Colors.black, + ), + ), + Text( + 'Mit existierendem Konto anmelden', + style: TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ], + ), + onPressed: () => Navigator.pushNamed(context, LoginPage.tag), + foregroundColor: Colors.grey, + backgroundColor: Colors.white, + borderSide: BorderSide( + color: Colors.grey[300]!, + width: 1, + ), + ); + } +} + +class _BaseButton extends StatelessWidget { + const _BaseButton({ + super.key, + required this.text, + required this.onPressed, + required this.backgroundColor, + required this.foregroundColor, + this.borderSide = BorderSide.none, + }); + + final Widget text; + final VoidCallback onPressed; + final BorderSide borderSide; + final Color backgroundColor; + final Color foregroundColor; + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: BoxConstraints( + minWidth: MediaQuery.of(context).textScaleFactor * 300, + ), + child: ElevatedButton( + onPressed: onPressed, + child: Padding( + padding: const EdgeInsets.all(12), + child: DefaultTextStyle.merge( + // Even though the text is already centered with `DefaultTextStyle` + // of the `_Bottom` widget`, we need to set `textAlign` to `center` + // here, because otherwise the text will be aligned to the left. + textAlign: TextAlign.center, + child: text, + style: TextStyle( + // For the golden tests we need to set the font family again, + // because otherwise 'Ahem' will be used. + // + // Can be removed when the following bug is resolved: + // https://github.com/eBay/flutter_glove_box/issues/103. + fontFamily: rubik, + ), + ), + ), + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: borderSide, + ), + foregroundColor: foregroundColor, + backgroundColor: backgroundColor, + textStyle: const TextStyle( + fontWeight: FontWeight.normal, + fontSize: 18, + ), + elevation: 0, + shadowColor: Colors.transparent, + ), + ), + ); + } +} diff --git a/app/lib/onboarding/welcome_page.dart b/app/lib/onboarding/welcome_page.dart index 15e2fab60..a1c39f087 100644 --- a/app/lib/onboarding/welcome_page.dart +++ b/app/lib/onboarding/welcome_page.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart' hide VerticalDivider; import 'package:sharezone/auth/login_page.dart'; +import 'package:sharezone/onboarding/mobile_welcome_page.dart'; import 'package:sharezone_utils/platform.dart'; import 'sign_up/sign_up_page.dart'; @@ -21,6 +22,6 @@ class WelcomePage extends StatelessWidget { @override Widget build(BuildContext context) { if (PlatformCheck.isDesktopOrWeb) return LoginPage.desktop(); - return SignUpPage(withLogin: true, withBackButton: false); + return MobileWelcomePage(); } } diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.iphone11.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.iphone11.png new file mode 100644 index 000000000..1e0e7fa12 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.iphone11.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone.png new file mode 100644 index 000000000..7c90a0dd0 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone_landscape.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone_landscape.png new file mode 100644 index 000000000..7495a545f Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.phone_landscape.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_landscape.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_landscape.png new file mode 100644 index 000000000..2caf7cd03 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_landscape.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_portrait.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_portrait.png new file mode 100644 index 000000000..1c7389e69 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_dark.tablet_portrait.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.iphone11.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.iphone11.png new file mode 100644 index 000000000..1e0e7fa12 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.iphone11.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone.png new file mode 100644 index 000000000..7c90a0dd0 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone_landscape.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone_landscape.png new file mode 100644 index 000000000..7495a545f Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.phone_landscape.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_landscape.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_landscape.png new file mode 100644 index 000000000..2caf7cd03 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_landscape.png differ diff --git a/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_portrait.png b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_portrait.png new file mode 100644 index 000000000..1c7389e69 Binary files /dev/null and b/app/test_goldens/onboarding/goldens/mobile_welcome_page_light.tablet_portrait.png differ diff --git a/app/test_goldens/onboarding/mobile_welcome_page_test.dart b/app/test_goldens/onboarding/mobile_welcome_page_test.dart new file mode 100644 index 000000000..8212cecd1 --- /dev/null +++ b/app/test_goldens/onboarding/mobile_welcome_page_test.dart @@ -0,0 +1,40 @@ +// Copyright (c) 2022 Sharezone UG (haftungsbeschränkt) +// Licensed under the EUPL-1.2-or-later. +// +// You may obtain a copy of the Licence at: +// https://joinup.ec.europa.eu/software/page/eupl +// +// SPDX-License-Identifier: EUPL-1.2 + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:golden_toolkit/golden_toolkit.dart'; +import 'package:sharezone/onboarding/mobile_welcome_page.dart'; +import 'package:sharezone_widgets/sharezone_widgets.dart'; + +void main() { + group(MobileWelcomePage, () { + Future _pumpPage(WidgetTester tester, {ThemeData? theme}) async { + await tester.pumpWidgetBuilder( + MobileWelcomePage(), + wrapper: materialAppWrapper(theme: theme), + ); + } + + testGoldens('renders as expected (light theme)', (tester) async { + await _pumpPage(tester, theme: lightTheme); + + await multiScreenGolden(tester, 'mobile_welcome_page_light'); + }); + + // At the moment, the screen is hard coded to dark mode. This test will just + // ensures that the screen is still rendered with the correct colors. + // + // Ticket: https://github.com/SharezoneApp/sharezone-app/issues/916 + testGoldens('renders as expected (dark theme)', (tester) async { + await _pumpPage(tester, theme: darkTheme); + + await multiScreenGolden(tester, 'mobile_welcome_page_dark'); + }); + }); +}