Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: match platform scrolling behavior with Flutter using HTML scrollable area #77883

Open
FelixMittermeier opened this issue Mar 11, 2021 · 21 comments
Labels
a: quality A truly polished experience c: new feature Nothing broken; request for a new capability c: performance Relates to speed or footprint issues (see "perf:" labels) c: proposal A detailed proposal for a change to Flutter customer: crowd Affects or could affect many people, though not necessarily a specific customer. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. P3 Issues that are less important to the Flutter project platform-web Web applications specifically team-web Owned by Web platform team triaged-web Triaged by Web platform team

Comments

@FelixMittermeier
Copy link

The own scrolling functionality on Flutter Web has already caused problems a few times. The biggest disadvantage of the current implementation is the fact that it just doesn't feel natural. The speed, the acceleration and the bouncing when you reach the end, for example, are for the most part very different from "real" web pages.
So yesterday in the shower, I got an idea on how to fix all these problems in one go, and I thought it might be good to have a Flutter developer take a closer look at this idea:

In the background of the Flutter web application there should be an empty and invisible real HTML element and when the user scrolls in the browser e.g. with the mouse wheel, one should scroll directly inside this element. Then you just read with Javascript using a listener on which position this HTML scrolling element is currently located and then pass this position to the Flutter Canvas Kit where this value is then passed e.g. to the position.pixels value of a ScrollController, which then actually scrolls the app.

The big advantage of this would be that the scrolling would feel exactly the same as its implementation in every browser. In this way one would not "reinvent the wheel", but simply use what already exists and what has already been successfully tested and used.

Thanks for reading. Maybe that would be something that might be realized.

@Wissperwind
Copy link

Wissperwind commented Mar 11, 2021

Interesting Idea. In my opinion scrolling is defenetly the most annoying thing in flutter web at the moment.
My problem is just, that the scrolling is not smooth. On click rotation at the mouse whele moves the page instantly. Like in 2000. I think, how hard can it be to add a little animation with animate to...

@darshankawar darshankawar added the in triage Presently being triaged by the triage team label Mar 11, 2021
@darshankawar
Copy link
Member

@FelixMittermeier
Thanks for coming up this with idea / issue. There's an active open umbrella issue to discuss and fix scrollbar issues related to web and desktop and I think you should comment this idea there to get traction and for easy tracking at one place.

I am going ahead and closing this as duplicate in favor of umbrella issue. If you disagree, write in comments and I'll reopen to address it.

@darshankawar darshankawar added r: duplicate Issue is closed as a duplicate of an existing issue and removed in triage Presently being triaged by the triage team labels Mar 11, 2021
@FelixMittermeier
Copy link
Author

Well I do not think that this is related to the scrollbar itself. It is about the scrolling functionality in general so the linked issue does not fit in my opinion.

@darshankawar darshankawar reopened this Mar 12, 2021
@darshankawar
Copy link
Member

You are right. I probably mis-read the umbrella issue.

@darshankawar darshankawar added platform-web Web applications specifically c: performance Relates to speed or footprint issues (see "perf:" labels) a: quality A truly polished experience c: proposal A detailed proposal for a change to Flutter c: new feature Nothing broken; request for a new capability f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. passed first triage and removed r: duplicate Issue is closed as a duplicate of an existing issue labels Mar 12, 2021
@darshankawar
Copy link
Member

Related issues:

#56257
#32338
#77714
#75850
#59404

@yjbanov
Copy link
Contributor

yjbanov commented Mar 18, 2021

Thanks for the idea. It's an interesting idea. Currently the architecture of Flutter engine and framework prefers a different approach. However, this may have potential. If anything, this may be implementable using platform views as a package.

@yjbanov yjbanov changed the title Idea to improve the scrolling functionality on web drastically Idea: match platform scrolling behavior with Flutter using HTML scrollable area Mar 18, 2021
@yjbanov yjbanov added the P6 label Mar 18, 2021
@yjbanov
Copy link
Contributor

yjbanov commented Mar 19, 2021

A similar idea #64501

@Tofil
Copy link

Tofil commented Mar 19, 2021

A similar idea #64501

Maybe the other one is better as it adds scrolling fidelity as well as better performance, because scrolling is happening outside the main thread in most browsers.

@AryashDubey
Copy link

Hey are their any updates on this?

@tomgilder
Copy link
Contributor

We've implemented syncing of a HTML scroll area and a Flutter scroll area in an app. When scrolling a <div>, the ScrollController is updated to keep it in sync.

The code we have is for a very niche case, but I can confirm this is possible and does work pretty well. The scrolling feels a lot better and more "native".

I'll try to separate it out into a generic POC soon.

@tomgilder
Copy link
Contributor

Okay, managed to turn this into a very early preview package.

Live example: https://native-scroll.web.app
Package: https://pub.dev/packages/native_scroll
Code: https://github.com/tomgilder/native_scroll

Only tested on macOS with Safari and Chrome. Will probably break under lots of conditions. Almost certainly not mobile-friendly yet.

But it at least works on HTML and CanvasKit renderers.

@FelixMittermeier
Copy link
Author

@tomgilder Wow, thank you so much for the great work.
I tried it out and it looks very promising. Seems like the performance is a bit worse but apart from that...
Maybe it would be more performant if one could implement it at a lower level of flutter.
I'm already looking forward to how this idea is developing 😄

@jmshrv
Copy link

jmshrv commented Jul 29, 2021

Could this be implemented on other platforms? Desktop (or at least Linux when I tested it) has the same scroll behaviour as web, and Android/iOS scrolling still isn't perfect. It's much better than Web, but some people think that it's still slightly off.

Edit: in fact, are there other elements that could be improved by using native stuff? For example, text input and selecting text still don't feel quite right sometimes, so having the native platform do the work there would make sense. The challenge would be doing it without breaking custom behaviour (such as custom ScrollPhysics).

@talski
Copy link

talski commented Dec 18, 2021

Okay, managed to turn this into a very early preview package.

Live example: https://native-scroll.web.app Package: https://pub.dev/packages/native_scroll Code: https://github.com/tomgilder/native_scroll

Only tested on macOS with Safari and Chrome. Will probably break under lots of conditions. Almost certainly not mobile-friendly yet.

But it at least works on HTML and CanvasKit renderers.

Nice work, I'm on windows, so I needed to hide the ugly system scrollbar and add the flutter one:

Wrap your ListView or whatever ScrollView you are using with Scrollbar()

Add this to the header in web/index.html

<style>
  ::-webkit-scrollbar {
    display: none;
  }
</style>

But my drag to scroll stopped to work when I using your package, any thoughts on how to make it work again?

@chipweinberger
Copy link
Contributor

chipweinberger commented Jul 29, 2022

Flutter Native Scroll package does work for simple use cases, and it's code is very simple! Just wanted to copy it here for easy reference as pub.dev does not have easy online code viewing.

https://pub.dev/packages/native_scroll

native_scroll_web.dart
@JS()
library native_scroll;

import 'package:flutter/widgets.dart';
import 'package:js/js.dart';
import 'dart:html';
import 'dart:ui' as ui;

class NativeScrollBuilder extends StatefulWidget {
  final Widget Function(BuildContext context, ScrollController controller)
      builder;

  const NativeScrollBuilder({
    Key? key,
    required this.builder,
  }) : super(key: key);

  @override
  _NativeScrollBuilderState createState() => _NativeScrollBuilderState();
}

class _NativeScrollBuilderState extends State<NativeScrollBuilder> {
  static int _globalId = 0;

  late String _viewId;
  late ScrollController _scrollController;

  final _heightDiv = DivElement();

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _globalId++;
    _viewId = 'native-scroll-view-$_globalId';

    ui.platformViewRegistry.registerViewFactory(
      _viewId,
      (_) {
        // Create a scroll container - this is a <div> with scrolling overflow.
        // When it scroll, the Flutter ScrollController gets updated.
        return DivElement()
          ..id = _viewId
          ..style.overflow = 'scroll'
          ..style.width = '100%'
          ..style.height = '100%'
          // We need to cancel scroll events to stop them getting to Flutter
          ..onWheel.listen((event) => event.stopPropagation())
          ..onMouseWheel.listen((event) => event.stopPropagation())
          ..onScroll.listen((event) {
            final target = event.target as DivElement;
            _onNativeScroll(target.scrollTop);
          })
          ..append(_heightDiv);
      },
    );

    _scrollController.addListener(_onFlutterScroll);

    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) async {
      _setScrollHeight();

      _scrollController.position.addListener(() {
        _setScrollHeight();
      });
    });
  }

  void _onFlutterScroll() {
    // There was a scroll in Flutter (e.g. scrollController.jumpTo called)
    // We need to update our shadow HTML scrolling elements
    _setScrollHeight();

    _heightDiv.scrollTop = _scrollController.position.pixels.toInt();
  }

  void _onNativeScroll(int scrollTop) {
    // There was a scroll in HTML land, update Flutter
    _scrollController.jumpTo(scrollTop.toDouble());
  }

  double? _lastScrollHeight;

  void _setScrollHeight() {
    // This updates a child <div> to have the same height
    // as the scroll contents from Flutter

    final scrollContentsHeight = _scrollController.position.viewportDimension +
        _scrollController.position.maxScrollExtent;

    if (scrollContentsHeight != _lastScrollHeight) {
      _lastScrollHeight = scrollContentsHeight;
      _heightDiv.style.height = '${scrollContentsHeight}px';
    }
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        ScrollConfiguration(
          behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
          child: widget.builder(context, _scrollController),
        ),
        HtmlElementView(viewType: _viewId),
      ],
    );
  }
}
native_scroll_vm.dart
import 'package:flutter/widgets.dart';

class NativeScrollBuilder extends StatefulWidget {
  final Widget Function(BuildContext context, ScrollController controller)
      builder;

  const NativeScrollBuilder({
    Key? key,
    required this.builder,
  }) : super(key: key);

  @override
  _NativeScrollBuilderState createState() => _NativeScrollBuilderState();
}

class _NativeScrollBuilderState extends State<NativeScrollBuilder> {
  late ScrollController _scrollController;

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, _scrollController);
  }
}

NativeScrollBuilder(
    builder: (BuildContext context, ScrollController controller) {
    return ListView.builder(
        controller: controller,
        itemCount: 100,
        itemBuilder: (context, _) {
            return Text('I scroll nicely on the web');
        },
    ),
);

@yjbanov yjbanov added the customer: crowd Affects or could affect many people, though not necessarily a specific customer. label Mar 2, 2023
@NelepovDmitry
Copy link

So what is solution?

If flutter has problems with Instagram browser, Telegram browser and etc.

Maybe problem not in browsers? Problem is on flutter.

And HTML works fine. So scroll problem it's just in core flutter scrolling mechanism?

@ibsbk
Copy link

ibsbk commented Jul 6, 2023

So what is solution?

If flutter has problems with Instagram browser, Telegram browser and etc.

Maybe problem not in browsers? Problem is on flutter.

And HTML works fine. So scroll problem it's just in core flutter scrolling mechanism?

my workaround with same problem:

css:

body {
  height: 10000vh !important;
  position: static !important;
  overscroll-behavior: none;
}
flt-glass-pane {
  position: fixed !important;
  max-width: 100vw !important;
  max-height: 100vh !important;
}

js:

window.scroll(0, window.document.body.scrollHeight);

@flutter-triage-bot flutter-triage-bot bot added multiteam-retriage-candidate team-web Owned by Web platform team triaged-web Triaged by Web platform team labels Jul 8, 2023
@AlexTwice
Copy link

Hey @NelepovDmitry,
did you manage to fix it?

Also using a Flutter webapp inside of Telegram webapp, but the scrolling just keeps closing my webapp... -.-

Would appreciate some tips :)

@WBgLyErCMc
Copy link

my workaround with same problem:

css:

body {
  height: 10000vh !important;
  position: static !important;
  overscroll-behavior: none;
}
flt-glass-pane {
  position: fixed !important;
  max-width: 100vw !important;
  max-height: 100vh !important;
}

js:

window.scroll(0, window.document.body.scrollHeight);

Greetings @ibsbk

I found the compiled code in your repositories and it works fine
That's incredible!)

But unfortunately your hack doesn't work properly on my hello world code((

  • Good: telegram window does not minimize when scrolling down.
  • Bad: any interaction with app elements doesn't work

Could you please advise what I'm doing wrong or give me the minimal code
Thanks

main.dart
void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'FlutterWebApp',
    themeMode: ThemeMode.dark,
    darkTheme: ThemeData.dark(),
    home: Scaffold(
      body: ListView(
        children: [
          ...List.generate(
            30,
            (index) => ListTile(
              title: Text('ListTile #$index'),
            ),
          )
        ],
      ),
    ),
  );
}
}
index.html
<!-- ... -->
<script src="https://telegram.org/js/telegram-web-app.js" defer=""></script>
<script src="js.js" defer></script>

<!--hahahahah-->
<style>
  body {
    height: 10000vh !important;
    position: static !important;
    overscroll-behavior: none;
  }

  flt-glass-pane {
    position: fixed !important;
    max-width: 100vw !important;
    max-height: 100vh !important;

  }
</style>
<!-- ... -->
js.js
function resize_frame(height){
  console.log('height is ', height)
  document.body.style.height = `${height}px`
  window.Telegram.WebView.postEvent('resize_frame', false, {height: height});
}

window.scroll(0, window.document.body.scrollHeight);

@ibsbk
Copy link

ibsbk commented Mar 18, 2024

Hi @WBgLyErCMc

I had same problem after update on flutter 3.16 (or 3.13). In my case it was enough replace element name in css
flutter-view instead flt-glass-pane

@AlexTwice
Copy link

@ibsbk omg, thank you very much! ♥️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: quality A truly polished experience c: new feature Nothing broken; request for a new capability c: performance Relates to speed or footprint issues (see "perf:" labels) c: proposal A detailed proposal for a change to Flutter customer: crowd Affects or could affect many people, though not necessarily a specific customer. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. P3 Issues that are less important to the Flutter project platform-web Web applications specifically team-web Owned by Web platform team triaged-web Triaged by Web platform team
Projects
None yet
Development

No branches or pull requests