fix: improve scroll handling on home page to avoid unintentional scroll event detection
This commit is contained in:
		@@ -8,9 +8,11 @@ import 'package:furman_now/src/widgets/scroll_view_height.dart';
 | 
			
		||||
 | 
			
		||||
class HomeContent extends StatefulWidget {
 | 
			
		||||
  final bool collapse;
 | 
			
		||||
  final ScrollController controller;
 | 
			
		||||
 | 
			
		||||
  const HomeContent({
 | 
			
		||||
    required this.collapse,
 | 
			
		||||
    required this.controller,
 | 
			
		||||
    Key? key,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
@@ -19,13 +21,12 @@ class HomeContent extends StatefulWidget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _HomeContentState extends State<HomeContent> {
 | 
			
		||||
  final ScrollController _controller = ScrollController();
 | 
			
		||||
  bool _collapse = false;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (widget.collapse != _collapse) {
 | 
			
		||||
      _controller.animateTo(
 | 
			
		||||
      widget.controller.animateTo(
 | 
			
		||||
        0,
 | 
			
		||||
        duration: const Duration(milliseconds: 400),
 | 
			
		||||
        curve: Curves.easeInOut,
 | 
			
		||||
@@ -35,7 +36,7 @@ class _HomeContentState extends State<HomeContent> {
 | 
			
		||||
 | 
			
		||||
    return LayoutBuilder(
 | 
			
		||||
      builder: (context, constraints) => ScrollViewWithHeight(
 | 
			
		||||
        controller: _controller,
 | 
			
		||||
        controller: widget.controller,
 | 
			
		||||
        child: Align(
 | 
			
		||||
          alignment: Alignment.bottomCenter,
 | 
			
		||||
          child: TweenAnimationBuilder<double>(
 | 
			
		||||
@@ -50,7 +51,10 @@ class _HomeContentState extends State<HomeContent> {
 | 
			
		||||
                  decoration: const BoxDecoration(
 | 
			
		||||
                    color: Colors.white,
 | 
			
		||||
                    borderRadius:
 | 
			
		||||
                    BorderRadius.vertical(top: Radius.circular(30)),
 | 
			
		||||
                      BorderRadius.vertical(
 | 
			
		||||
                        top: Radius.circular(30),
 | 
			
		||||
                        bottom: Radius.circular(30),
 | 
			
		||||
                      ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  padding: const EdgeInsets.only(bottom: 15),
 | 
			
		||||
                  margin: EdgeInsets.only(top: margin),
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@ class HomePageHeader extends StatelessWidget {
 | 
			
		||||
                      color: Color(0xff26183d),
 | 
			
		||||
                      fontSize: 36,
 | 
			
		||||
                      fontWeight: FontWeight.w800,
 | 
			
		||||
                      height: 1.2,
 | 
			
		||||
                    )),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import 'package:auto_route/auto_route.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:furman_now/src/routes/index.gr.dart';
 | 
			
		||||
import 'package:furman_now/src/screens/home/content.dart';
 | 
			
		||||
import 'package:furman_now/src/screens/home/state.dart';
 | 
			
		||||
@@ -13,75 +14,124 @@ class HomeScreen extends StatefulWidget {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _HomeScreenState extends State<HomeScreen> {
 | 
			
		||||
  bool _collapse = false;
 | 
			
		||||
  final ScrollController _controller = ScrollController();
 | 
			
		||||
 | 
			
		||||
  double overscrollTopAmount = 0;
 | 
			
		||||
  double overscrollBottomAmount = 0;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
 | 
			
		||||
    _controller.addListener(updateScrollPosition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateScrollPosition() {
 | 
			
		||||
    // bottom overscroll
 | 
			
		||||
    if (_controller.position.pixels > _controller.position.maxScrollExtent) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        overscrollBottomAmount =
 | 
			
		||||
            _controller.position.pixels - _controller.position.maxScrollExtent;
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      if (overscrollBottomAmount != 0) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          overscrollBottomAmount = 0;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // top overscroll
 | 
			
		||||
    if (_controller.position.pixels < 0) {
 | 
			
		||||
      setState(() {
 | 
			
		||||
        overscrollTopAmount =
 | 
			
		||||
            _controller.position.pixels.abs();
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      if (overscrollTopAmount != 0) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          overscrollTopAmount = 0;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleScrollEvent(BuildContext context) {
 | 
			
		||||
    const scrollMessageSensitivity = 20;
 | 
			
		||||
    const pageSwitchSensitivity = 60;
 | 
			
		||||
    var offsetAmount = overscrollTopAmount.abs();
 | 
			
		||||
 | 
			
		||||
    if (overscrollTopAmount != 0) {
 | 
			
		||||
      if (offsetAmount > pageSwitchSensitivity) {
 | 
			
		||||
        context.router.navigate(const StudentIdRoute());
 | 
			
		||||
        context.read<HomePageState>().collapse = true;
 | 
			
		||||
      } else if (offsetAmount > scrollMessageSensitivity) {
 | 
			
		||||
        context.read<HomePageState>().showScrollMessage = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        context.read<HomePageState>().showScrollMessage = false;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      context.read<HomePageState>().showScrollMessage = false;
 | 
			
		||||
 | 
			
		||||
      if (_controller.position.pixels > pageSwitchSensitivity) {
 | 
			
		||||
        context.router.navigate(const HomeRoute());
 | 
			
		||||
        context.read<HomePageState>().collapse = false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return ChangeNotifierProvider(
 | 
			
		||||
      create: (context) => HomePageState(),
 | 
			
		||||
      builder: (context, _) => Scaffold(
 | 
			
		||||
        body: Container(
 | 
			
		||||
          color: const Color(0xffb7acc9),
 | 
			
		||||
          child: SafeArea(
 | 
			
		||||
          body: AnnotatedRegion<SystemUiOverlayStyle>(
 | 
			
		||||
            value: SystemUiOverlayStyle.light,
 | 
			
		||||
            child: Container(
 | 
			
		||||
              color: const Color(0xffb7acc9),
 | 
			
		||||
              child: Stack(
 | 
			
		||||
                fit: StackFit.loose,
 | 
			
		||||
                children: [
 | 
			
		||||
                  const AutoTabsRouter(
 | 
			
		||||
                    routes: [
 | 
			
		||||
                      HomeRoute(),
 | 
			
		||||
                      StudentIdRoute(),
 | 
			
		||||
              child: SafeArea(
 | 
			
		||||
                child: Container(
 | 
			
		||||
                  color: const Color(0xffb7acc9),
 | 
			
		||||
                  child: Stack(
 | 
			
		||||
                    fit: StackFit.loose,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      // overscroll indicator color
 | 
			
		||||
                      Align(
 | 
			
		||||
                        alignment: Alignment.bottomCenter,
 | 
			
		||||
                        child: Container(
 | 
			
		||||
                          height: overscrollBottomAmount + 30,
 | 
			
		||||
                          color: Colors.grey.shade50,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      const AutoTabsRouter(
 | 
			
		||||
                        routes: [
 | 
			
		||||
                          HomeRoute(),
 | 
			
		||||
                          StudentIdRoute(),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                      NotificationListener<ScrollNotification>(
 | 
			
		||||
                        onNotification: (notification) {
 | 
			
		||||
                          if (notification is ScrollUpdateNotification) {
 | 
			
		||||
                            handleScrollEvent(context);
 | 
			
		||||
                          }
 | 
			
		||||
                          return true;
 | 
			
		||||
                        },
 | 
			
		||||
                        child: Consumer<HomePageState>(
 | 
			
		||||
                          builder: (context, state, _) =>
 | 
			
		||||
                            ClipRect(child: HomeContent(
 | 
			
		||||
                              controller: _controller,
 | 
			
		||||
                              collapse: state.collapse,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                  NotificationListener<ScrollNotification>(
 | 
			
		||||
                    onNotification: (notification) {
 | 
			
		||||
                      if (notification is ScrollUpdateNotification) {
 | 
			
		||||
                        final offset = notification.metrics.pixels;
 | 
			
		||||
                        const scrollMessageSensitivity = 20;
 | 
			
		||||
                        const pageSwitchSensitivity = 60;
 | 
			
		||||
                        if (offset < 0) {
 | 
			
		||||
                          var offsetAmount = offset.abs();
 | 
			
		||||
                          if (offsetAmount > pageSwitchSensitivity) {
 | 
			
		||||
                            context.router.navigate(const StudentIdRoute());
 | 
			
		||||
                            setState(() {
 | 
			
		||||
                              _collapse = true;
 | 
			
		||||
                            });
 | 
			
		||||
                          } else if (offsetAmount > scrollMessageSensitivity) {
 | 
			
		||||
                            if (!context.read<HomePageState>().showScrollMessage) {
 | 
			
		||||
                              context.read<HomePageState>().showScrollMessage = true;
 | 
			
		||||
                            }
 | 
			
		||||
                          } else {
 | 
			
		||||
                            if (context.read<HomePageState>().showScrollMessage) {
 | 
			
		||||
                              context.read<HomePageState>().showScrollMessage = false;
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                        } else {
 | 
			
		||||
                          var offsetAmount = offset.abs();
 | 
			
		||||
                          if (offsetAmount > pageSwitchSensitivity) {
 | 
			
		||||
                            context.router.navigate(const HomeRoute());
 | 
			
		||||
                            setState(() {
 | 
			
		||||
                              _collapse = false;
 | 
			
		||||
                            });
 | 
			
		||||
                          } else {
 | 
			
		||||
                            if (context.read<HomePageState>().showScrollMessage) {
 | 
			
		||||
                              context.read<HomePageState>().showScrollMessage = false;
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
                      return true;
 | 
			
		||||
                    },
 | 
			
		||||
                    child: ClipRect(child: HomeContent(
 | 
			
		||||
                      collapse: _collapse,
 | 
			
		||||
                    )),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,21 @@ import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class HomePageState extends ChangeNotifier {
 | 
			
		||||
  bool _showScrollMessage = false;
 | 
			
		||||
  bool _collapse = false;
 | 
			
		||||
 | 
			
		||||
  bool get showScrollMessage {
 | 
			
		||||
    return _showScrollMessage;
 | 
			
		||||
  bool get showScrollMessage { return _showScrollMessage; }
 | 
			
		||||
  set showScrollMessage(bool value) {
 | 
			
		||||
    if (value != _showScrollMessage) {
 | 
			
		||||
      _showScrollMessage = value;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set showScrollMessage(bool value) {
 | 
			
		||||
    _showScrollMessage = value;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
  bool get collapse { return _collapse; }
 | 
			
		||||
  set collapse(bool value) {
 | 
			
		||||
    if (value != _collapse) {
 | 
			
		||||
      _collapse = value;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -29,6 +29,7 @@ class _RestaurantsListState extends State<RestaurantsList> {
 | 
			
		||||
                  itemCount: restaurants.length,
 | 
			
		||||
                  cacheExtent: 10000,
 | 
			
		||||
                  scrollDirection: Axis.horizontal,
 | 
			
		||||
                  physics: const BouncingScrollPhysics(),
 | 
			
		||||
                  prototypeItem: Padding(
 | 
			
		||||
                    padding: const EdgeInsets.only(right: 15),
 | 
			
		||||
                    child: RestaurantCard(restaurant: restaurants.first),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user