#flutter #flutter-web #mousewheel #smooth-scrolling #flutter-pageview
#flutter #flutter-web #колесо мыши #плавная прокрутка #flutter-просмотр страницы
Вопрос:
С помощью приведенного ниже кода
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp(
home: const MyHomePage(),
);
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) => DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Center(
child: Text('use the mouse wheel to scroll')),
bottom: TabBar(
tabs: const [
Center(child: Text('ScrollView')),
Center(child: Text('PageView'))
],
),
),
body: TabBarView(
children: [
SingleChildScrollView(
child: Column(
children: [
for (int i = 0; i < 10; i )
Container(
height: MediaQuery.of(context).size.height,
child: const Center(
child: FlutterLogo(size: 80),
),
),
],
),
),
PageView(
scrollDirection: Axis.vertical,
children: [
for (int i = 0; i < 10; i)
const Center(
child: FlutterLogo(size: 80),
),
],
),
],
),
),
);
}
Вы можете увидеть, запустив его на dartpad или из этого видео,
использование колеса мыши для прокрутки PageView
обеспечивает посредственный опыт (в лучшем случае),
Это известная проблема # 35687 # 32120, но я пытаюсь найти обходной путь
для достижения либо плавной прокрутки для PageView
, либо, по крайней мере, предотвращения «заикания».
Может ли кто-нибудь помочь мне или указать правильное направление?
Я не уверен, что проблема в PageScrollPhysics
;
У меня есть внутреннее ощущение, что проблема может быть в wheelEvent
поскольку прокрутка с помощью мультитач работает отлично
Комментарии:
1. вот очень неудачный взлом dartpad.dartlang.org/c3476573514298b9885b78a0275c744c
Ответ №1:
Проблема возникает из-за цепочки событий:
- пользователь поворачивает колесо мыши на одну ступеньку,
Scrollable
получаетPointerSignal
и вызываетjumpTo
метод,_PagePosition
Метод jumpTo (производный отScrollPositionWithSingleContext
) обновляет положение прокрутки и вызываетgoBallistic
метод,- запрошенный из
PageScrollPhysics
моделирования возвращает позицию обратно к исходному значению, поскольку полученное смещение на одну отметку слишком мало для переворачивания страницы, - еще одна отметка и процесс, повторяемый с шага (1).
Один из способов устранить проблему — выполнить задержку перед вызовом goBallistic
метода. Это можно сделать в классе _PagePosition, однако класс является частным, и мы должны исправить Flutter SDK:
// <FlutterSDK>/packages/flutter/lib/src/widgets/page_view.dart
// ...
class _PagePosition extends ScrollPositionWithSingleContext implements PageMetrics {
//...
// add this code to fix issue (mostly borrowed from ScrollPositionWithSingleContext):
Timer timer;
@override
void jumpTo(double value) {
goIdle();
if (pixels != value) {
final double oldPixels = pixels;
forcePixels(value);
didStartScroll();
didUpdateScrollPositionBy(pixels - oldPixels);
didEndScroll();
}
if (timer != null) timer.cancel();
timer = Timer(Duration(milliseconds: 200), () {
goBallistic(0.0);
timer = null;
});
}
// ...
}
Другой способ — заменить jumpTo на animateTo. Это можно сделать без исправления Flutter SDK, но выглядит сложнее, потому что нам нужно отключить PointerSignalEvent
прослушиватель по умолчанию:
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class PageViewLab extends StatefulWidget {
@override
_PageViewLabState createState() => _PageViewLabState();
}
class _PageViewLabState extends State<PageViewLab> {
final sink = StreamController<double>();
final pager = PageController();
@override
void initState() {
super.initState();
throttle(sink.stream).listen((offset) {
pager.animateTo(
offset,
duration: Duration(milliseconds: 200),
curve: Curves.ease,
);
});
}
@override
void dispose() {
sink.close();
pager.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Mouse Wheel with PageView'),
),
body: Container(
constraints: BoxConstraints.expand(),
child: Listener(
onPointerSignal: _handlePointerSignal,
child: _IgnorePointerSignal(
child: PageView.builder(
controller: pager,
scrollDirection: Axis.vertical,
itemCount: Colors.primaries.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(color: Colors.primaries[index]),
);
},
),
),
),
),
);
}
Stream<double> throttle(Stream<double> src) async* {
double offset = pager.position.pixels;
DateTime dt = DateTime.now();
await for (var delta in src) {
if (DateTime.now().difference(dt) > Duration(milliseconds: 200)) {
offset = pager.position.pixels;
}
dt = DateTime.now();
offset = delta;
yield offset;
}
}
void _handlePointerSignal(PointerSignalEvent e) {
if (e is PointerScrollEvent amp;amp; e.scrollDelta.dy != 0) {
sink.add(e.scrollDelta.dy);
}
}
}
// workaround https://github.com/flutter/flutter/issues/35723
class _IgnorePointerSignal extends SingleChildRenderObjectWidget {
_IgnorePointerSignal({Key key, Widget child}) : super(key: key, child: child);
@override
RenderObject createRenderObject(_) => _IgnorePointerSignalRenderObject();
}
class _IgnorePointerSignalRenderObject extends RenderProxyBox {
@override
bool hitTest(BoxHitTestResult result, {Offset position}) {
final res = super.hitTest(result, position: position);
result.path.forEach((item) {
final target = item.target;
if (target is RenderPointerListener) {
target.onPointerSignal = null;
}
});
return res;
}
}
Вот демо на CodePen.
Комментарии:
1. Я пытался использовать animateTo, но я получил эту ошибку: в page_view. _PagePosition.new.получить пиксели [как пиксели] ( localhost: 49511/packages / flutter/src/widgets /… ) в main_layout. _MainLayoutState.new.throttle ( localhost:49511/packages/one_page/… ) в throttle.next (<анонимный>) в _AsyncStarImpl.new.runBody ( localhost:49511/dart_sdk.js:31858:40 ) в Object._microtaskLoop dart_sdk.js:39175:13) в _startMicrotaskLoop (dart_sdk.js: 39181:13) на локальном хостинге: 49511/dart_sdk.js:34688:9
Ответ №2:
Очень похоже, но проще в настройке:
добавьте smooth_scroll_web ^ 0.0.4 в свой pubspec.yaml
...
dependencies:
...
smooth_scroll_web: ^0.0.4
...
Использование:
import 'package:smooth_scroll_web/smooth_scroll_web.dart';
import 'package:flutter/material.dart';
import 'dart:math'; // only for demo
class Page extends StatefulWidget {
@override
PageState createState() => PageState();
}
class PageState extends State<Page> {
final ScrollController _controller = new ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SmoothScroll Example"),
),
body: SmoothScrollWeb(
controller: controller,
child: Container(
height: 1000,
child: ListView(
physics: NeverScrollableScrollPhysics(),
controller: _controller,
children: [
// Your content goes here, thoses children are only for demo
for (int i = 0; i < 100; i )
Container(
height: 60,
color: Color.fromARGB(1,
Random.secure().nextInt(255),
Random.secure().nextInt(255),
Random.secure().nextInt(255)),
),
],
),
),
),
);
}
}
Спасибо тебе, хоббист!
Обратитесь к проблеме flutter # 32120 на Github.
Комментарии:
1. спасибо, я в курсе проблемы github.com/flutter/flutter/issues/32120#issuecomment-725913608
2. также я настоятельно рекомендую НЕ использовать пакеты без тестов gitlab.com/dezso15/smoothscrollweb/-/tree/master /…
3. Мне нравится эта библиотека, она намного улучшает прокрутку с помощью flutter, но только сегодня я заметил, что если я прокручиваю с помощью контроллера прокрутки, плавная прокрутка не «замечает» этого, и когда я пытаюсь прокрутить колесо мыши, оно телепортирует меня обратно в то место, где я нажал на кнопку (которая использовала контроллер прокрутки для прокрутки)
Ответ №3:
Я знаю, что с этого вопроса прошло почти 1,5 года, но я нашел способ, который работает бесперебойно. Возможно, это будет очень полезно для тех, кто это прочитает. Добавьте прослушиватель к вашему контроллеру просмотра страниц с помощью этого кода (вы можете вносить изменения в продолжительность или следующую страницу / animateToPage / jumpToPage и т.д.):
pageController.addListener(() {
if (pageController.position.userScrollDirection == ScrollDirection.reverse) {
pageController.nextPage(duration: const Duration(milliseconds: 60), curve: Curves.easeIn);
} else if (pageController.position.userScrollDirection == ScrollDirection.forward) {
pageController.previousPage(duration: const Duration(milliseconds: 60), curve: Curves.easeIn);
}
});
Ответ №4:
Проблема заключается в пользовательских настройках, в том, как конечный пользователь настроил прокрутку с помощью своей мыши. У меня есть мышь Logitech, которая позволяет мне включать или выключать функцию плавной прокрутки с помощью опций Logitech. Когда я включаю плавную прокрутку, она работает отлично и прокручивается по мере необходимости, но в случае отключения плавной прокрутки она также отключается в проекте. Поведение задано конечным пользователем.
Тем не менее, если требуется принудительно выполнить прокрутку для плавной прокрутки, это можно сделать, только установив соответствующие анимации. На данный момент прямого способа нет.
Комментарии:
1. извините, приятель, но я боюсь, что вы упускаете мою точку зрения, как вы можете видеть из обеих проблем, которые я связал выше, либо «wheelEvent», либо «PageViewScrollPhysics» не работают должным образом, это утверждение, а НЕ вопрос. Я уверен, что в конечном итоге ошибка будет исправлена / реализована функция, я ищу обходной путь или какую-нибудь полезную информацию о том, как исправить это самостоятельно, или даже просто смягчить проблему. Указание пользователям изменить настройки мыши НЕ является РЕШЕНИЕМ