GithubHelp home page GithubHelp logo

fujidaiti / exprollable_page_view Goto Github PK

View Code? Open in Web Editor NEW
67.0 2.0 4.0 69.02 MB

Yet another PageView widget that expands its viewport as it scrolls. Exprollable is a coined word combining the words expandable and scrollable.

Home Page: https://pub.dev/packages/exprollable_page_view

License: MIT License

Kotlin 0.09% Swift 0.91% Objective-C 0.03% Dart 64.63% CMake 13.98% C++ 16.92% C 1.06% HTML 1.38% Ruby 1.00%
dart flutter flutter-ui flutterpackage

exprollable_page_view's People

Contributors

fujidaiti avatar khoadng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

exprollable_page_view's Issues

Cannot Unexpand on Web

Everything works pretty great on web, except once an item has been scrolled to expand, it does not unxpand like it does on mobile (when you swipe down).

Adaptive / Widescreen Mode

If you use the Books app on an iPad, you will see that in Landscape mode, it never goes full screen and the UI adapts so that there is more viewport fraction visibility on adjacent slides/books. Not sure if the is a package thing, or a custom implementation.

Anyhow, this is a very great useful package, but it's very awkward to use in Landscape mode as is.

Thanks

AbsorbScrollPosition keeps holding disposed ScrollAbsorber

A AbsorbScrollPosition may lives longer than the AbsorbScrollController that it had been attached to. In that case, it may uses the attached ScrollAbsorber which is already disposed by its owner AbsorbScrollController. Then, it causes the following exception

══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
A ScrollAbsorber was used after being disposed.
Once you have called dispose() on a ScrollAbsorber, it can no longer be used.

When the exception was thrown, this was the stack:
#0      ChangeNotifier.debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:157:9)
#1      ChangeNotifier.debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:164:6)
#2      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:360:27)
#3      ScrollAbsorber.notifyListeners (package:exprollable_page_view/src/internal/scroll.dart:60:13)
#4      ScrollAbsorber.absorb (package:exprollable_page_view/src/internal/scroll.dart:51:7)
#5      AbsorbScrollPosition.setPixels (package:exprollable_page_view/src/internal/scroll.dart:287:14)
#6      AbsorbScrollPosition.applyUserOffset (package:exprollable_page_view/src/internal/scroll.dart:236:5)
#7      ScrollDragController.update (package:flutter/src/widgets/scroll_activity.dart:387:14)
#8      ScrollableState._handleDragUpdate (package:flutter/src/widgets/scrollable.dart:719:12)
#9      DragGestureRecognizer._checkUpdate.<anonymous closure> (package:flutter/src/gestures/monodrag.dart:483:55)
#10     GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24)
#11     DragGestureRecognizer._checkUpdate (package:flutter/src/gestures/monodrag.dart:483:7)
#12     DragGestureRecognizer.handleEvent (package:flutter/src/gestures/monodrag.dart:330:9)
#13     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12)
#14     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9)
#15     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:625:13)
#16     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
#17     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7)
#18     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:460:19)
#19     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22)
#20     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:336:11)
#21     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7)
#22     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5)
#23     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7)
#24     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7)
#25     _invoke1 (dart:ui/hooks.dart:164:13)
#26     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:361:7)
#27     _dispatchPointerDataPacket (dart:ui/hooks.dart:91:31)

Handler: "onUpdate"
Recognizer:
  VerticalDragGestureRecognizer#73cbc

A weird ViewportInset issue

I got a case where I need to use keyboard inside PageView, initialInset: const ViewportInset.fractional(0.9),

The pages are dynamically generated, at least 1 page will be build,
but while there's exactly 1 page, the keyboard will covered a bit of space inside page,
until I slide the page so slightly that it won't close & force rebuild itself.
While if I got >= 2 pages to be build, this weird behaviour doesn't present at all, works normally.

ExprollablePageController.jumpViewportOffsetTo doesn't work

import 'package:example/src/common.dart';
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  late final controller = ExprollablePageController(
    initialPage: 0,
    maxViewportOffset: ViewportOffset.shrunk,
    minViewportFraction: 0.8,
    snapViewportOffsets: [
      ViewportOffset.expanded,
      ViewportOffset.shrunk,
    ],
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
              onPressed: () =>
                  controller.jumpViewportOffsetTo(ViewportOffset.expanded),
              icon: Icon(Icons.keyboard_double_arrow_down))
        ],
      ),
      bottomNavigationBar: const ExampleBottomAppBar(),
      body: ExprollablePageView(
        controller: controller,
        itemCount: 5,
        itemBuilder: (context, page) {
          return ExampleListView(
            controller: PageContentScrollController.of(context),
            page: page,
          );
        },
      ),
    );
  }
}

The viewport fraction exceeds the upper limit when the page is fully expanded

The code to reproduce this issue:
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: Home());
  }
}

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late ExprollablePageController controller;

  @override
  void initState() {
    super.initState();
    controller = ExprollablePageController(
      viewportConfiguration: ViewportConfiguration(
        minFraction: 0.9,
        maxFraction: 1.0,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ExprollablePageView(
        controller: controller,
        itemBuilder: (context, page) {
          final viewport = ExprollablePageController.of(context)!.viewport;
          return ColoredBox(
            color: Colors.blueAccent,
            child: SingleChildScrollView(
              controller: PageContentScrollController.of(context),
              physics: const AlwaysScrollableScrollPhysics(),
              child: SizedBox(
                height: viewport.pageDimensions.maxHeight,
                child: Placeholder(
                  child: Center(
                    child: ValueListenableBuilder(
                      valueListenable: viewport,
                      builder: (_, metrics, __) => Text(
                        "Fraction = ${metrics.fraction.toStringAsFixed(3)}\n"
                        "BottomPadding = ${metrics.dimensions.padding.bottom}\n"
                        "ViewportHeight = ${metrics.dimensions.height}\n"
                        "MaxPageHeight = ${metrics.pageDimensions.maxHeight}\n"
                        "ApparentPageHeight = ${(metrics.pageDimensions.maxHeight * metrics.fraction).toStringAsFixed(3)}",
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                    ),
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

The result:

Simulator Screen Recording - iPhone 14 Pro - 2023-06-12 at 02 19 46

In the above GIF, the apparent page height exceeds the max height (818px) when the page is fully expanded (852px).

Add a factory constructor for customizing snap behavior

If we want to add an additional snap position of the viewport offset, we would typically write code as follows, but it's a bit verbose.

const peekOffset = ViewportOffset.fractional(0.5);
controller = ExprollablePageController(
  initialViewportOffset: peekOffset,
  maxViewportOffset: peekOffset,
  snapViewportOffsets: [
    ViewportOffset.expanded,
    ViewportOffset.shrunk,
    peekOffset,
  ],
);

It would be nice to have a factory constructor of ExprollablePageController for simplicity like:

const peekOffset = ViewportOffset.fractional(0.5);
controller = ExprollablePageController.withAdditionalSnapOffsets(
  initialViewportOffset: peekOffset,
  additionalSnapViewportOffsets: [ peekOffset ],
);

Hero Animation Not Working As Expected

I thought the Hero was a visual transition from one element into the next page. I expect the thumbnail image to visually transition from the small thumbnail into the fullsize image when expanded. also it should reverse so you can see the image shrink back down into the thumbnail.

how do we get this working?

thanks

Disabling and then enabling scrolling prevents the viewport from expanding

Video

Screen.Recording.2023-04-18.at.20.20.47.mov
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  var enableScroll = true;
  var expanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Text(enableScroll ? "Scrolling Enable" : "Scrolling Disable")),
      floatingActionButton: FloatingActionButton(
        onPressed: () => setState(() => enableScroll = !enableScroll),
        child: Icon(Icons.add),
      ),
      bottomSheet: BottomSheet(
        onClosing: () {},
        builder: (context) => Container(
          height: 80,
          child: AppBar(title: Text(expanded ? "Expanded" : "Shrunk")),
        ),
      ),
      body: ExprollablePageView(
        physics: enableScroll ? null : NeverScrollableScrollPhysics(),
        onViewportChanged: (metrics) =>
            setState(() => expanded = metrics.isExpanded),
        itemCount: 5,
        itemBuilder: (context, page) {
          return ListView.builder(
            physics: enableScroll ? null : NeverScrollableScrollPhysics(),
            itemCount: 100,
            controller: PageContentScrollController.of(context),
            itemBuilder: (context, index) =>
                ListTile(title: Text(index.toString())),
          );
        },
      ),
    );
  }
}

setState will cause ConcurrentModificationError

import 'package:example/src/common.dart';
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  var count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton:
          FloatingActionButton(onPressed: () => setState(() => count++)),
      bottomNavigationBar: const ExampleBottomAppBar(),
      body: ExprollablePageView(
        itemCount: 5,
        itemBuilder: (context, page) {
          return ExampleListView(
            controller: PageContentScrollController.of(context),
            page: page,
          );
        },
      ),
    );
  }
}

Right - Left swipe during the full size screen

Discussed in #43

Originally posted by VeskoDev June 14, 2023
First of all, I have to say that the package works perfectly, but what I need is a scroll system that works on the initial opening of the page view, which has a standard page view. My question is, what would be the easiest way and does this package provide the capability to scroll from page to page even though I'm on a full-size page?

How to tap inset to dismiss and tap region to expand?

Is it possible to

  1. dismiss by tapping the top inset
  2. enlarge(expand) the card by tapping the card

using ModalExprollableRouteBuilder.

Navigator.of(context).push(
ModalExprollableRouteBuilder(
pageBuilder: (context, _, __) => ExprollablePageView(...),
),
);

I try to set barrierDismissible to true, but not working.
ModalExprollableRouteBuilder(
barrierDismissible: true,
pageBuilder: (context, _, __) => const CustomView(),),

Thanks.

About Shrunk and Expanded States

I need to know which state it is currently in to perform some action when the state changes from Expanded to Shrunk.

Is there a built-in way that I can use? Any guidance or examples would be greatly appreciated.

Thank you in advance!

ExprollablePageController's state resetting on rebuild

The ExprollablePageController is not able to keep its state like the TextEditingController.
Therefore, every time the ExprollablePageView is rebuilt, the controller is seems to be re-initialized and losing its previous state.

Screen.Recording.2023-04-16.at.14.54.36.mov
import 'package:example/src/common.dart';
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  var count = 0;
  late final controller = ExprollablePageController(
    initialPage: 0,
    maxViewportOffset: ViewportOffset.shrunk,
    minViewportFraction: 0.8,
    snapViewportOffsets: [
      ViewportOffset.expanded,
      ViewportOffset.shrunk,
    ],
  );
  final textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton:
          FloatingActionButton(onPressed: () => setState(() => count++)),
      bottomNavigationBar: const ExampleBottomAppBar(),
      body: count % 2 == 0
          ? ExprollablePageView(
              controller: controller,
              itemCount: 5,
              itemBuilder: (context, page) {
                return Column(
                  children: [
                    TextField(
                      controller: textEditingController,
                    ),
                    Expanded(
                      child: ExampleListView(
                        controller: PageContentScrollController.of(context),
                        page: page,
                      ),
                    ),
                  ],
                );
              },
            )
          : Center(
              child: Container(
                color: Colors.red,
                child: TextField(
                  controller: textEditingController,
                ),
              ),
            ),
    );
  }
}

Multiple calls to ExprollablePageController.animateViewportOffsetTo during the animation can cause it to become stuck.

Video:

Screen.Recording.2023-05-04.at.22.40.56.mov
import 'package:example/src/common.dart';
import 'package:exprollable_page_view/exprollable_page_view.dart';
import 'package:flutter/material.dart';

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  late final controller = ExprollablePageController(
    initialPage: 0,
    maxViewportOffset: ViewportOffset.shrunk,
    minViewportFraction: 0.8,
    snapViewportOffsets: [
      ViewportOffset.expanded,
      ViewportOffset.shrunk,
    ],
  );
  var expanded = false;
  var title = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: [
          if (expanded)
            IconButton(
                onPressed: () {
                  setState(() {
                    title = "animate to shrunk";
                  });
                  controller.animateViewportOffsetTo(ViewportOffset.shrunk,
                      curve: Curves.easeOut,
                      duration: Duration(milliseconds: 150));
                },
                icon: Icon(Icons.keyboard_double_arrow_up))
          else
            IconButton(
                onPressed: () {
                  setState(() {
                    title = "animate to expanded";
                  });
                  controller.animateViewportOffsetTo(ViewportOffset.expanded,
                      curve: Curves.easeOut,
                      duration: Duration(milliseconds: 150));
                },
                icon: Icon(Icons.keyboard_double_arrow_down))
        ],
      ),
      bottomNavigationBar: const ExampleBottomAppBar(),
      body: ExprollablePageView(
        onViewportChanged: (metrics) => setState(() {
          expanded = metrics.isExpanded;
        }),
        controller: controller,
        itemCount: 5,
        itemBuilder: (context, page) {
          return ExampleListView(
            controller: PageContentScrollController.of(context),
            page: page,
          );
        },
      ),
    );
  }
}

Remove AlwaysFillViewportPageView and use the original PageView with OverflowBox

I fond that AlwaysFillViewportPageView widget can be replaced with the original PageView with OverflowBox as follows. It's much more simpler than the current implementation and doesn't require copying and pasting the original source code of PageView. It should be also possible to rewrite the logic of overshoot effect using OverflowBox.

https://api.flutter.dev/flutter/widgets/OverflowBox-class.html

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(useMaterial3: true),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

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

class _MyHomePageState extends State<MyHomePage> {
  static const viewportFraction = 0.8;
  final controller = PageController(
    viewportFraction: viewportFraction,
  );

  @override
  Widget build(BuildContext context) {
    final colors = [
      Colors.red,
      Colors.yellow,
      Colors.green,
      Colors.blue,
    ];
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) {
          return PageView.builder(
            controller: controller,
            itemBuilder: (context, page) {
              return Transform.scale(
                scale: viewportFraction,
                child: OverflowBox(
                  minWidth: constraints.minWidth,
                  maxWidth: constraints.maxWidth,
                  minHeight: constraints.minHeight,
                  maxHeight: constraints.maxHeight,
                  child: ColoredBox(
                    color: colors[page % colors.length].withOpacity(0.7),
                    child: const Placeholder(),
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

The result:

Simulator Screen Recording - iPhone 14 Pro - 2023-05-22 at 14 00 12

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.