espresso3389 / pdfrx

pdfrx is yet another PDF viewer implementation that built on the top of pdfium. The plugin currently supports Android, iOS, Windows, macOS, Linux, and Web.
MIT License
62 stars 38 forks source link

Show/hide ScrollThumb like Flutter's default ScrollBar #67

Closed shahmirzali49 closed 3 months ago

shahmirzali49 commented 3 months ago

How can I disable always always-visible ScrollThumb? is it possible or can you add this feature?

espresso3389 commented 3 months ago

It's just implemented in the example code. Remove the following lines will disable the ScrollThumb:

https://github.com/espresso3389/pdfrx/blob/75aa49f576b6df3f6ea17a93d7c1c9e9e7e1c2eb/example/lib/main.dart#L231-L263

All the thumb implementation is up to you.

shahmirzali49 commented 3 months ago

I mean like this. when you don't scroll ScrollThumb disappear :

https://github.com/espresso3389/pdfrx/assets/49102327/73dc97b3-b708-41d1-a03b-3a94ede67f64

espresso3389 commented 3 months ago

I understand what you are talking about. But it's not my task. It's your task.

espresso3389 commented 3 months ago

OK, I should explain my position.

All I have to do is that providing APIs to enable user to implement features. All the examples are to explain how to do that.

I provided PdfViewerScrollThumb and its source code. You can improve the behavior using your Flutter skill only. If you have any questions about how to use pdfrx or any problems, please ask me anything about that.

shahmirzali49 commented 3 months ago

For those interested, here's how I implemented the solution:

class ReadingDetailPage extends HookConsumerWidget {
  const ReadingDetailPage({required this.pdfUrl, super.key});
  final String pdfUrl;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final controller = useRef(PdfViewerController());
    final isScrolling = useState(false);
    Timer? scrollHideTimer;

    void startScrollHideTimer() {
      isScrolling.value = true;
      scrollHideTimer?.cancel();
      scrollHideTimer = Timer(Duration(seconds: 2), () {
        isScrolling.value = false;
      });
    }

    useEffect(() {
      return scrollHideTimer?.cancel;
    }, const []);
    return CloudBackgroundWidget(
      child: Material(
        color: Colors.transparent,
        child: SafeArea(
          bottom: true,
          top: false,
          left: false,
          right: false,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                height: context.topPadding == 0 ? 20.spMin : context.topPadding,
              ),
              CustomAppbar(
                title: Text(
                  'Kitab adı',
                  style: context.textTheme.headline3!.copyWith(
                    fontFamily: FontFamily.P22Bangersfield_Bold.value,
                  ),
                ),
              ),
              16.verticalSpace,
              Expanded(
                child: PdfViewer.uri(
                  Uri.parse(
                      'https://www.diva-portal.org/smash/get/diva2:1560848/FULLTEXT01.pdf'),
                  controller: controller.value,
                  params: PdfViewerParams(
                    viewerOverlayBuilder: (context, size) {
                      return [
                        // Show vertical scroll thumb on the right; it has page number on it
                        if (isScrolling.value)
                          PdfViewerScrollThumb(
                            controller: controller.value,
                            orientation: ScrollbarOrientation.right,
                            thumbSize: Size(12.w, 70.h),
                            margin: 4,
                            thumbBuilder:
                                (context, thumbSize, pageNumber, controller) =>
                                    Container(
                              decoration: BoxDecoration(
                                color: Colors.grey,
                                borderRadius: BorderRadius.circular(32.r),
                              ),
                              margin: EdgeInsets.only(top: 6.h),
                            ),
                          ),
                      ];
                    },
                    backgroundColor: Colors.white,
                    boundaryMargin: EdgeInsets.zero,
                    margin: 0,
                    loadingBannerBuilder:
                        (context, bytesDownloaded, totalBytes) => Center(
                      child: CircularProgressIndicator(
                        value: totalBytes != null
                            ? bytesDownloaded / totalBytes
                            : null,
                        backgroundColor: Colors.grey.shade200,
                      ),
                    ),
                    onViewerReady: (document, readyController) async {
                      readyController.addListener(() {
                        startScrollHideTimer();
                        log("scrolling");
                      });
                    },
                    errorBannerBuilder:
                        (context, error, stackTrace, documentRef) {
                      return Center(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text('Error: $error'),
                            ElevatedButton(
                              onPressed: () {},
                              child: const Text('Reload'),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
espresso3389 commented 3 months ago

As a side node, PdfViewerController is a ValueListenable and you can addListener to be notified for scrolling events.

shahmirzali49 commented 3 months ago

@espresso3389 thank you for your quick reply and information.

shahmirzali49 commented 3 months ago

@espresso3389 there is just one thing is that, in the original scrollbar, When you click/hold and scroll with thumb it will not disappear until you leave. in my code it's not possible. can you add listener for thumb ?

shahmirzali49 commented 3 months ago

I added GestureDetector to Thumb but after this, scrollthumb not working

PdfViewerScrollThumb(
                          controller: controller.value,
                          orientation: ScrollbarOrientation.right,
                          thumbSize: Size(12.w, 70.h),
                          margin: 4,
                          thumbBuilder:
                              (context, thumbSize, pageNumber, controller) =>
                                  GestureDetector(
                            onPanEnd: (details) {
                              print("end");
                            },
                            onPanStart: (xyz) {
                              print("start");
                            },
                            child: Container(
                              decoration: BoxDecoration(
                                color: Colors.grey,
                                borderRadius: BorderRadius.circular(32.r),
                              ),
                              margin: EdgeInsets.only(top: 6.h),
                            ),
                          ),
                        ),

https://github.com/espresso3389/pdfrx/assets/49102327/39ccf25f-e844-493e-9c35-dbc4ac1bc6f3

espresso3389 commented 3 months ago

OK, I'll show you everything is already there. The following is the minimum example to show/hide scroll thumb:

class _MainPageState extends State<MainPage> {
  final controller = PdfViewerController();
  Timer? thumbHideTimer;
  final showThumb = ValueNotifier<bool>(false);
  bool thumbGrabbed = false;

  @override
  void dispose() {
    controller.removeListener(_scrollDetector);
    thumbHideTimer?.cancel();
    showThumb.dispose();
    super.dispose();
  }

  void _scrollDetector() {
    showThumb.value = true;
    _hideScrollThumbLater();
  }

  void _hideScrollThumbLater() {
    thumbHideTimer?.cancel();
    thumbHideTimer = Timer(const Duration(milliseconds: 500),
        () => showThumb.value = thumbGrabbed);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Pdfrx example')),
      body: PdfViewer.asset(
        'assets/PDF32000_2008.pdf',
        controller: controller,
        params: PdfViewerParams(
          enableTextSelection: true,
          maxScale: 8,
          viewerOverlayBuilder: (context, size) => [
            // Show vertical scroll thumb on the right; it has page number on it
            PdfViewerScrollThumb(
              controller: controller,
              orientation: ScrollbarOrientation.right,
              thumbSize: const Size(40, 25),
              thumbBuilder: (context, thumbSize, pageNumber, controller) =>
                  ValueListenableBuilder(
                valueListenable: showThumb,
                builder: (context, showThumbValue, child) {
                  return AnimatedOpacity(
                    duration: showThumbValue
                        ? const Duration(milliseconds: 200)
                        : const Duration(milliseconds: 2000),
                    opacity: showThumbValue ? 1.0 : 0.0,
                    child: Listener(
                      behavior: HitTestBehavior.translucent,
                      onPointerDown: (_) {
                        thumbGrabbed = true;
                        showThumb.value = true;
                        _hideScrollThumbLater();
                      },
                      onPointerUp: (_) {
                        thumbGrabbed = false;
                        _hideScrollThumbLater();
                      },
                      child: Container(
                        color: Colors.black,
                        child: Center(
                          child: Text(
                            pageNumber.toString(),
                            style: const TextStyle(color: Colors.white),
                          ),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            Positioned.fill(
              child: Listener(
                behavior: HitTestBehavior.translucent,
                onPointerHover: (event) {
                  if (controller.viewSize.width - 30 < event.localPosition.dx) {
                    showThumb.value = true;
                    _hideScrollThumbLater();
                  }
                },
              ),
            ),
          ],
          onViewerReady: (document, controller) async {
            controller.addListener(_scrollDetector);
          },
        ),
      ),
    );
  }
}

Yes, it's a little complicated but not impossible.

shahmirzali49 commented 3 months ago

thank you