Using SingleChildScrollView Inside PageView

Viewed 2482

In my app, I have a PageView that each page contains a page of a book. Naturally, all of the pages are larger than the viewport, so I used SingleChildScrollView inside each page so I can scroll the content of the page. The problem is that I can't scroll to the next page of the PageView when I get to the bottom of the page (It tries to continue scrolling the page itself and not the page view). Is there a way to make my app scroll the page when reaching the page vertical borders? (both the top and the bottom border.)

Here's my code

import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:penguinovel/core/source/chapter.dart';

class ChapterPage extends StatelessWidget {

  final List<Chapter> chapters;
  final int initialIndex;

  ChapterPage(this.chapters, this.initialIndex);

  @override
  Widget build(BuildContext context){

    return Scaffold(
        appBar: AppBar(title: Text(this.chapters[this.initialIndex].name, style: TextStyle(color: Colors.white),), backgroundColor: Colors.black,),
        body: PageView.builder(
          controller: PageController(initialPage: this.initialIndex),
          scrollDirection: Axis.vertical,
          itemCount: this.chapters.length,
          itemBuilder: (BuildContext context, int index) => buildChapter(this.chapters[index]),
        )
      );

  }

  Widget buildChapter(Chapter chapter) {
    return FutureBuilder<String>(future: chapter.getContent(), builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
      if(snapshot.hasData){
          return SingleChildScrollView(child: Html(data: snapshot.data));
      } else if (snapshot.hasError) {
          return Center(child: Text(snapshot.error.toString()));
      } else {
        return Center(child: CircularProgressIndicator());
      }
    });
  }
}

Thanks in advance :)

2 Answers

After trying flutter_easyrefresh and another lib called pull_to_refresh (I think). I've stumbled into many bugs that I've tried to fix. During this process I've learnt about a thing called BouncingScrollPhysics.

This gives a scrollable object some out of bounds content that bounces back when the user stops holding his finger.

Using that, I've create a ScrollController and a listener that waits for the scrolling to be out of bounds and after some tweaks... it worked :)

Here's the code snippet

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:penguinovel/core/source/chapter.dart';



class ChapterPage extends StatefulWidget {

  final List<Chapter> chapters;
  final int initialIndex;

  ChapterPage(this.chapters, this.initialIndex);

  _ChapterPageState createState() => _ChapterPageState(this.initialIndex);
}


class _ChapterPageState extends State<ChapterPage> {

  int _index;
  PageController _pageController;
  ScrollController _scrollController;

  _ChapterPageState(this._index);

  @override
  void initState(){
    super.initState();
    _pageController = PageController(initialPage: widget.initialIndex);
    _scrollController = ScrollController();
    _scrollController.addListener(this.swapPageListener);
  }

  @override
  Widget build(BuildContext context){

    return Scaffold(
      appBar: AppBar(title: Text(widget.chapters[this._index].name, style: TextStyle(color: Colors.white),), backgroundColor: Colors.black,),
      body: PageView.builder(
        controller: _pageController,
        scrollDirection: Axis.vertical,
        itemBuilder: (BuildContext context, int index) {
        return FutureBuilder<String>(future: widget.chapters[index].getContent(), builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if(snapshot.hasData){
              return this.buildSingleChapterPage(snapshot.data);
          } else if (snapshot.hasError) {
              return Center(child: Text(snapshot.error.toString()));
          } else {
            return Center(child: CircularProgressIndicator());
          }
        });
      }));

  }

  Widget buildSingleChapterPage(String data){
    return SingleChildScrollView(
        child: Html(data: data),
        physics: BouncingScrollPhysics(),
        controller: _scrollController
      );
  }

  void swapPageListener() {
    if (_scrollController.offset > _scrollController.position.maxScrollExtent + 170) {
      if (this._index < widget.chapters.length - 1 ){
        setState(() {
          this._index ++;
          _pageController.jumpToPage(this._index);
        });
      }
   }

   if (_scrollController.offset < _scrollController.position.minScrollExtent - 170) {
      if (this._index > 0){
        setState(() {
          this._index --;
          _pageController.jumpToPage(this._index);
        });
      }
   }
  }
}

Thank you for your suggestions and I hope it will help other people as well.

Try use flutter_easyrefresh, implements the pageview jumptopage by controller in onRefresh (previous page) & onLoad (next page).

Related