Home > Blockchain >  Flutter - PageView - Don't change page if the user still touches the screen
Flutter - PageView - Don't change page if the user still touches the screen

Time:07-19

How to update the PageView to trigger onPageChange only on specific conditions?

Here, I don't want to change the current page if the user is still touching the screen. Apart from that, everything should remain the same (ballistic scroll simulation, page limits)

It seems it has to deal with the ScrollPhysics object attached to PageView, but I don't know how to correctly extends it.

Let me know if you need some code, but the question is very general and can refer to any PageView, so you should not need any context.

Minimum Reproductible Example

Here is the translation in dart of the text above. Feel free to update this code to make it achieve the objective.

// main.dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(title: _title, home: MyPageView());
  }
}

class MyPageView extends StatefulWidget {
  const MyPageView({Key? key}) : super(key: key);

  @override
  State<MyPageView> createState() => _MyPageViewState();
}

class _MyPageViewState extends State<MyPageView> {
  @override
  Widget build(BuildContext context) {
    final PageController controller = PageController();
    return Scaffold(
        body: SafeArea(
            child: PageView.builder(
      onPageChanged: (int index) {
        // TODO: Don't trigger this function if you still touch the screen
        print('onPageChanged index $index, ${controller.page}');
      },
      allowImplicitScrolling: false,
      controller: controller,
      itemBuilder: (BuildContext context, int index) {
        print('Build Sliver');
        return Center(
          child: Text('Page $index'),
        );
      },
    )));
  }
}

Example of a (bad) solution

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(title: _title, home: MyPageView());
  }
}

class MyPageView extends StatefulWidget {
  const MyPageView({Key? key}) : super(key: key);

  @override
  State<MyPageView> createState() => _MyPageViewState();
}

class _MyPageViewState extends State<MyPageView> {
  @override
  Widget build(BuildContext context) {
    final PageController controller = PageController();
    return Scaffold(
        body: SafeArea(
      child: Listener(
        onPointerUp: (PointerUpEvent event) {
          if (controller.page == null) {
            return;
          }

          if (controller.page! > 0.5) {
            //TODO: update the time so it fits the end of the animation
            Future.delayed(const Duration(milliseconds: 700), () {
              print('Do your custom action onPageChange action here');
            });
          }
        },
        child: PageView.builder(
          controller: controller,
          itemBuilder: (BuildContext context, int index) {
            print('Build Sliver');
            return Center(
              child: Text('Page $index'),
            );
          },
        ),
      ),
    ));
  }
}

This solution triggers an action on the next page, 700ms after the user stops touching the screen.

It does work, but it is a lousy work.

  1. How to account for different screen sizes? 700ms is the maximum amount of time to animate between 2 pages on an iPhone SE.
  2. How to adjust this arbitrary number (700), so it varies according to controller.page (the closer to the next page, the smaller you have to wait).
  3. It doesn't use onHorizontalDragEnd or a similar drag detector, which can result in unwanted behaviour.

CodePudding user response:

You should disable the scrolling entirely on Screenshot

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
      debugShowCheckedModeBanner: false,
      scrollBehavior: MyCustomScrollBehavior(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onHorizontalDragEnd: (details) => (details.primaryVelocity ?? 0) < 0
            ? _pageController.nextPage(
                duration: const Duration(seconds: 1), curve: Curves.easeInOut)
            : _pageController.previousPage(
                duration: const Duration(seconds: 1), curve: Curves.easeInOut),
        child: PageView(
          physics: const NeverScrollableScrollPhysics(),
          controller: _pageController,
          children: [
            Container(
              color: const Color.fromARGB(255, 0, 91, 187),
            ),
            Container(
              color: const Color.fromARGB(255, 255, 213, 0),
            ),
          ],
        ),
      ),
    );
  }
}

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
      };
}

CodePudding user response:

You can simply use physics: NeverScrollableScrollPhysics() inside PageView() to achieve this kind of behaviour

  • Related