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

[PageView] Add/Remove page while staying on current page #58959

Open
fralways opened this issue Jun 8, 2020 · 14 comments
Open

[PageView] Add/Remove page while staying on current page #58959

fralways opened this issue Jun 8, 2020 · 14 comments
Labels
c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. team-design Owned by Design Languages team triaged-design Triaged by Design Languages team

Comments

@fralways
Copy link

fralways commented Jun 8, 2020

Use case

Lets say I have an app which in first page of PageView shows list of suggestions based on location. If I select something I want to add a new page after first page which shows details about selected suggestion and to navigate user to the new page. This is fine and can be done. Now if I swipe more to the right and go back I would like for user to again see suggestions page and not details of selected suggestions. This is currently not possible because if I do any kind of modification of PageView pages which are before page user is currently looking at, user will see snap, and the page will automatically change. If I try to do something like controller.jumpToPage, after setting new page list, it will be okay but only if user has no gesture running at that point. If he is in middle of swiping, he will loose his gesture.

Proposal

We could have additional methods available via controller or something else where we can insert/delete pages without notifying user about it, meaning this insertion/deletion should be aware and update page and offset of PageView automatically when page list changes.

@iapicca
Copy link
Contributor

iapicca commented Jun 8, 2020

Hi @fralways
does not a simple StatefulWidget provide this feature?

code sample
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {

  static const Widget _home = HomePage();
  @override
  Widget build(BuildContext context) => MaterialApp(
    home: _home,
  );
}

class HomePage extends StatefulWidget {
  const HomePage();

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

class _HomePageState extends State<HomePage> {

  PageController _controller;
  List<int> _lenght = [0];
  @override
  void initState() { 
    super.initState();
    _controller = PageController();
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  void _add() => setState(() {
    _lenght.add(_lenght.length);
   
  });

  void _remove() => setState(() {
    if(_lenght.length > 1) {
      _lenght.removeLast();
    }
  });

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
            actions: <Widget>[
              Padding(
                padding: const EdgeInsets.only(right: 20.0),
                child: IconButton(
                  icon: const Icon(Icons.remove),
                  onPressed: _remove,
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(right: 20.0),
                child: IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: _add,
                ),
              ),
            ],
          ),
        body: PageView(
          controller: _controller,
          children: <Widget>[
            for(int i in _lenght) Center(
              child: Container(
                color: Colors.blueAccent[100],
                height: 80,
                width: 80,
                child: Center(child:Text('$i'),),
              ),
            ),
          ],
        ),
      );
}
Thank you

@iapicca iapicca added in triage Presently being triaged by the triage team waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds labels Jun 8, 2020
@fralways
Copy link
Author

fralways commented Jun 8, 2020

@iapicca I have modified a bit your code:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  static const Widget _home = HomePage();
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: _home,
      );
}

class HomePage extends StatefulWidget {
  const HomePage();

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

class _HomePageState extends State<HomePage> {
  PageController _controller;
  List<int> _lenght = [0, 1, 2];
  @override
  void initState() {
    super.initState();
    _controller = PageController(initialPage: 1);
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  void _add() => setState(() {
        _lenght.add(_lenght.length);
      });

  void _remove() => setState(() {
        if (_lenght.length > 1) {
          _lenght.removeAt(0);
        }
      });

  void _reset() => setState(() {
        _lenght = [0, 1, 2];
      });

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          actions: <Widget>[
            Padding(
              padding: const EdgeInsets.only(right: 20.0),
              child: IconButton(
                icon: const Icon(Icons.refresh),
                onPressed: _reset,
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(right: 20.0),
              child: IconButton(
                icon: const Icon(Icons.remove),
                onPressed: _remove,
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(right: 20.0),
              child: IconButton(
                icon: const Icon(Icons.add),
                onPressed: _add,
              ),
            ),
          ],
        ),
        body: PageView(
          controller: _controller,
          children: <Widget>[
            for (int i in _lenght)
              Padding(
                padding: const EdgeInsets.all(32.0),
                child: Container(
                  color: Colors.blueAccent[100],
                  alignment: Alignment.center,
                  child: Text(
                    '$i',
                    style: const TextStyle(fontSize: 32),
                  ),
                ),
              ),
          ],
        ),
      );
}

What I want is if I have 3 pages - 0, 1 and 2, and Im at page 1, and I delete page 0, I want to still to see page 1 since it should not matter to the user if page 0 is removed since he is not looking at it. What happens is if I remove page 0, PageView jumps from page 1 to page 2 since it does not update its page index / offset.

@no-response no-response bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Jun 8, 2020
@TahaTesser
Copy link
Member

Hi @fralways
it's jumping from page 1 to page 2 because current index is same but index is moved to page 2.
Do you think index shouldn't change when we remove/add page?
Thank you

@TahaTesser TahaTesser added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Jun 9, 2020
@fralways
Copy link
Author

fralways commented Jun 9, 2020

Yes, after add/delete I would like PageView not to change displayed page. I have not found any solution to accomplish this, thus I suggested additional methods in PageController. As I said, after adding/deleting page I tried to say controller.jumpToPage so that I can set again the correct page, but this breaks user gesture if its currently active.

@no-response no-response bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Jun 9, 2020
@iapicca
Copy link
Contributor

iapicca commented Jun 10, 2020

Hi @fralways
I didn't mean to provide "the best" implementation,
the point I wanted to make is that what you are proposing
seems more like an implementation that could or should
come down on the developer's implementation.

Making a parallel with list view, adding and removing items
it's a common occurrence and while there is a AnimatedList
the implementation seems to be inconsistent with this approach,
making the user "unaware" of the changes.

I'm not sure that it is desirable to bake this behaviour in the framework,
could you please describe the advantages that this approach would bring

Thank you

@iapicca iapicca added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Jun 10, 2020
@fralways
Copy link
Author

fralways commented Jun 10, 2020

@iapicca I understand that it was example to make a point but I did the same. I showed that it's not possible to change PageView pages without changing the page user is currently looking at. Currently using PageView it's not possible to remove pages which are not necessary anymore if those pages have index < current page index without changing current displayed page.

I really struggle to find a use case where a developer wants to remove some old page and after removal, current displayed page should change. This is how PageView currently works.

In parallel with iOS, there we have page view data source methods which are called only when pages are changing, where we can supply any page we want. That means if some page is not used anymore we just don't provide it and it's removed. This makes developer in control, while here we have no control over index once page list is rebuilt. Just imagine if we have 50 pages and the user is looking at 20th, and we need to remove first 10 because for some reason they expired / should not be displayed anymore, the widget will then jump to 30th page which breaks ux.

The same thing happens if using PageView.builder.

@no-response no-response bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Jun 10, 2020
@iapicca iapicca added f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. c: proposal A detailed proposal for a change to Flutter c: new feature Nothing broken; request for a new capability and removed in triage Presently being triaged by the triage team labels Jun 10, 2020
@dannyvalentesonos
Copy link
Contributor

I agree it would be nice to have this ability built into the PageView. However, as @iapicca points out, it should be possible for the developer to do this, and it does work. I have this working perfectly. However, Flutter 1.19.0-4.1.pre has broken this on Android specifically, See #62209

@chakrihacker
Copy link

Hi, I am also kind of facing same issue, where I want to insert children at the beginning of the list rather than end of the list.

  • One way is provide methods to add or remove items to list, by keeping current index intact
  • Or a method to update index, when length of the list changes

@rubenvereecken
Copy link

rubenvereecken commented Sep 7, 2020

I had the same or at least similar needs. Interestingly, I started off with a ListView.builder, which, for some reason, behaves differently and which I couldn't get to work properly/as expected.

Compare the original (more or less) to the builder version.

If I had to summarise the difference: I can't manage to maintain the same page (by jumping after a remove) or it seems to remove the wrong element.

@chaudharydeepanshu
Copy link

@iapicca One use case for this behavior would be used in calendars as we don't know the size of the month list. Generally, we don't ask the user to provide the end and start date of calendars before showing them to the user, we add more months to the start if the user swipes left and more months to the right if the user swipes right . The right swipe scenario is easy right now as it doesn't affect the current page but adding more months at the start on the left swipe would cause jumps.

@chaudharydeepanshu
Copy link

Now, coming to the current solution suggested above was jumping back to the previous page after adding a new page but this would lead to an unnecessary rebuild which leads to more jank and glitches.
The negative impact of this solution could be seen in this #108998 issue. (Notice the glitches in dates in the screen recording which are caused due to jumping on the previous page after insertion).

@chaudharydeepanshu
Copy link

Another alternate solution suggested by @goderbauer is using the center with a CustomScrollView described here #62209 (comment) which worked great but when I added a controller on it to identify user swipes and add more to the left or right I got a wired result as swiping left was getting scrolled back to the initial value.

Runnable gist showcasing the issue - https://gist.github.com/chaudharydeepanshu/26217550b19def87966bde16ffe05cd1

Video of the issue -

test2.mp4

So, @goderbauer if you have any solution for this issue then I request you to please let me know as this functionality is very important to me.

@chaudharydeepanshu
Copy link

chaudharydeepanshu commented Aug 5, 2022

Another alternate solution suggested by @goderbauer is using the center with a CustomScrollView described here #62209 (comment) which worked great but when I added a controller on it to identify user swipes and add more to the left or right I got a wired result as swiping left was getting scrolled back to the initial value.

Runnable gist showcasing the issue - https://gist.github.com/chaudharydeepanshu/26217550b19def87966bde16ffe05cd1

Video of the issue -

test2.mp4
So, @goderbauer if you have any solution for this issue then I request you to please let me know as this functionality is very important to me.

Somehow I did manage to fix this issue by using the ScrollController in place of PageController but that also has its own issues. The issue is that it auto scrolls on changing the size of the device or on changing the orientation of the device.

Issue video: -
Notice the change in months:

Untitled.Project2.mp4
test3.mp4

The issue is that the scroll controller uses offsets for scroll position and changing device size leads to scrolling as the offset is still old. Maybe we can fix this by listening to changes in device size and adding to offset? But I think that will be too much for a such simple task.

Also, I had to use global keys to get the width of the scroll view widgets to check if the user swiped was left or right and also how far left and how far right as it doesn't have the concept for getting the current page. Also, it lacks the functionality of setting the viewport. And that's why I think the scroll view is not a replacement for the pageview.

So, all of the above resulted in tedious workarounds and still, each of them failed to solve the problems so I think it would be a massive help if page view supports a feature to Add/Remove page while staying on the current page.

Code for the calendar view example in anyone wants to try - https://gist.github.com/chaudharydeepanshu/d12e8303f1d3920a8c8a6f4e8d0e3ba4

@vergaraSC
Copy link

any ideas how to fix this? i want to insert a new item at the position 0 but i want to preserve the current view without use jump because it's bad ux

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: new feature Nothing broken; request for a new capability c: proposal A detailed proposal for a change to Flutter f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. team-design Owned by Design Languages team triaged-design Triaged by Design Languages team
Projects
None yet
Development

No branches or pull requests

9 participants