Add scroll wheel support for intro screen

This commit is contained in:
Shawn 2023-07-31 10:14:04 -06:00
parent 7318ce7d9a
commit b6e35f6f92
2 changed files with 108 additions and 93 deletions

View File

@ -1,6 +1,7 @@
import 'package:flutter/gestures.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:wonders/common_libs.dart'; import 'package:wonders/common_libs.dart';
import 'package:wonders/ui/common/app_icons.dart'; import 'package:wonders/ui/common/app_icons.dart';
import 'package:wonders/ui/common/controls/app_page_indicator.dart'; import 'package:wonders/ui/common/controls/app_page_indicator.dart';
import 'package:wonders/ui/common/gradient_container.dart'; import 'package:wonders/ui/common/gradient_container.dart';
import 'package:wonders/ui/common/static_text_scale.dart'; import 'package:wonders/ui/common/static_text_scale.dart';
@ -25,6 +26,8 @@ class _IntroScreenState extends State<IntroScreen> {
late final PageController _pageController = PageController()..addListener(_handlePageChanged); late final PageController _pageController = PageController()..addListener(_handlePageChanged);
final ValueNotifier<int> _currentPage = ValueNotifier(0); final ValueNotifier<int> _currentPage = ValueNotifier(0);
bool get _isOnLastPage => _currentPage.value.round() == pageData.length - 1; bool get _isOnLastPage => _currentPage.value.round() == pageData.length - 1;
bool get _isOnFirstPage => _currentPage.value.round() == 0;
@override @override
void dispose() { void dispose() {
_pageController.dispose(); _pageController.dispose();
@ -48,12 +51,17 @@ class _IntroScreenState extends State<IntroScreen> {
duration: $styles.times.fast, curve: Curves.easeOut); duration: $styles.times.fast, curve: Curves.easeOut);
} }
void _handleNavTextDoubleTapped() { void _handleNavTextSemanticTap() => _incrementPage(1);
void _incrementPage(int dir){
final int current = _pageController.page!.round(); final int current = _pageController.page!.round();
if (_isOnLastPage) return; if (_isOnLastPage && dir > 0) return;
_pageController.animateToPage(current + 1, duration: 250.ms, curve: Curves.easeIn); if (_isOnFirstPage && dir < 0) return;
_pageController.animateToPage(current + dir, duration: 250.ms, curve: Curves.easeIn);
} }
void _handleScrollWheel(double delta) => _incrementPage(delta >0? 1 : -1);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Set the page data, as strings may have changed based on locale // Set the page data, as strings may have changed based on locale
@ -70,96 +78,103 @@ class _IntroScreenState extends State<IntroScreen> {
final List<Widget> pages = pageData.map((e) => _Page(data: e)).toList(); final List<Widget> pages = pageData.map((e) => _Page(data: e)).toList();
/// Return resulting widget tree /// Return resulting widget tree
return DefaultTextColor( return Listener(
color: $styles.colors.offWhite, onPointerSignal: (signal){
child: Container( if(signal is PointerScrollEvent){
color: $styles.colors.black, _handleScrollWheel(signal.scrollDelta.dy);
child: SafeArea( }
child: Animate( },
delay: 500.ms, child: DefaultTextColor(
effects: const [FadeEffect()], color: $styles.colors.offWhite,
child: Stack( child: Container(
children: [ color: $styles.colors.black,
// page view with title & description: child: SafeArea(
MergeSemantics( child: Animate(
child: Semantics( delay: 500.ms,
onIncrease: () => _handleSemanticSwipe(1), effects: const [FadeEffect()],
onDecrease: () => _handleSemanticSwipe(-1), child: Stack(
child: PageView( children: [
controller: _pageController, // page view with title & description:
children: pages, MergeSemantics(
onPageChanged: (_) => AppHaptics.lightImpact(), child: Semantics(
), onIncrease: () => _handleSemanticSwipe(1),
), onDecrease: () => _handleSemanticSwipe(-1),
), child: PageView(
controller: _pageController,
IgnorePointer( children: pages,
ignoringSemantics: false, onPageChanged: (_) => AppHaptics.lightImpact(),
child: Column(children: [
Spacer(),
// logo:
Semantics(
header: true,
child: Container(
height: _logoHeight,
alignment: Alignment.center,
child: _WonderousLogo(),
), ),
), ),
// masked image:
SizedBox(
height: _imageSize,
width: _imageSize,
child: ValueListenableBuilder<int>(
valueListenable: _currentPage,
builder: (_, value, __) {
return AnimatedSwitcher(
duration: $styles.times.slow,
child: KeyedSubtree(
key: ValueKey(value), // so AnimatedSwitcher sees it as a different child.
child: _PageImage(data: pageData[value]),
),
);
},
),
),
// placeholder gap for text:
Gap(_IntroScreenState._textHeight),
// page indicator:
Container(
height: _pageIndicatorHeight,
alignment: Alignment(0.0, 0),
child: AppPageIndicator(
count: pageData.length, controller: _pageController, color: $styles.colors.offWhite),
),
Spacer(flex: 2),
]),
),
// Build a cpl overlays to hide the content when swiping on very wide screens
_buildHzGradientOverlay(left: true),
_buildHzGradientOverlay(),
// finish button:
Positioned(
right: $styles.insets.lg,
bottom: $styles.insets.lg,
child: _buildFinishBtn(context),
),
// nav help text:
BottomCenter(
child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.lg),
child: _buildNavText(context),
), ),
),
], IgnorePointer(
ignoringSemantics: false,
child: Column(children: [
Spacer(),
// logo:
Semantics(
header: true,
child: Container(
height: _logoHeight,
alignment: Alignment.center,
child: _WonderousLogo(),
),
),
// masked image:
SizedBox(
height: _imageSize,
width: _imageSize,
child: ValueListenableBuilder<int>(
valueListenable: _currentPage,
builder: (_, value, __) {
return AnimatedSwitcher(
duration: $styles.times.slow,
child: KeyedSubtree(
key: ValueKey(value), // so AnimatedSwitcher sees it as a different child.
child: _PageImage(data: pageData[value]),
),
);
},
),
),
// placeholder gap for text:
Gap(_IntroScreenState._textHeight),
// page indicator:
Container(
height: _pageIndicatorHeight,
alignment: Alignment(0.0, 0),
child: AppPageIndicator(
count: pageData.length, controller: _pageController, color: $styles.colors.offWhite),
),
Spacer(flex: 2),
]),
),
// Build a cpl overlays to hide the content when swiping on very wide screens
_buildHzGradientOverlay(left: true),
_buildHzGradientOverlay(),
// finish button:
Positioned(
right: $styles.insets.lg,
bottom: $styles.insets.lg,
child: _buildFinishBtn(context),
),
// nav help text:
BottomCenter(
child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.lg),
child: _buildNavText(context),
),
),
],
),
), ),
), ),
), ),
@ -215,7 +230,7 @@ class _IntroScreenState extends State<IntroScreen> {
duration: $styles.times.fast, duration: $styles.times.fast,
child: Semantics( child: Semantics(
onTapHint: $strings.introSemanticNavigate, onTapHint: $strings.introSemanticNavigate,
onTap: _isOnLastPage ? null : _handleNavTextDoubleTapped, onTap: _isOnLastPage ? null : _handleNavTextSemanticTap,
child: Text($strings.introSemanticSwipeLeft, style: $styles.text.bodySmall), child: Text($strings.introSemanticSwipeLeft, style: $styles.text.bodySmall),
), ),
); );

View File

@ -1,7 +1,7 @@
name: wonders name: wonders
description: Explore the famous wonders of the world. description: Explore the famous wonders of the world.
publish_to: "none" publish_to: "none"
version: 2.0.16+5 version: 2.0.17+6
environment: environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"